Skip to content

Commit

Permalink
Improve performance when editing nodes (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
carlgieringer authored Dec 6, 2024
1 parent 0206965 commit 8af366f
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 24 deletions.
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions packages/browser-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@
"dependencies": {
"@reduxjs/toolkit": "^2.2.7",
"@sentry/browser": "^8.33.1",
"@sophistree/common": "file:../common",
"@sophistree/ui-common": "file:../ui-common",
"classnames": "^2.5.1",
"cytoscape": "^3.30.2",
"cytoscape-elk": "^2.2.0",
"deep-equal": "^2.2.3",
"esbuild-plugin-flow": "github:dalcib/esbuild-plugin-flow",
"@sophistree/common": "file:../common",
"@sophistree/ui-common": "file:../ui-common",
"immer": "^10.1.1",
"lodash.debounce": "^4.0.8",
"lodash.merge": "^4.6.2",
"react": "^18.3.1",
"react-art": "^18.3.1",
Expand All @@ -59,6 +60,7 @@
"@types/chrome": "^0.0.270",
"@types/deep-equal": "^1.0.4",
"@types/jest": "^29.5.13",
"@types/lodash.debounce": "^4.0.9",
"@types/lodash.merge": "^4.6.9",
"@types/node": "^22.5.4",
"@types/react": "^18.3.5",
Expand Down
55 changes: 41 additions & 14 deletions packages/browser-extension/src/components/EntityEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from "react";
import React, { useMemo, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { TextInput, Text, Surface } from "react-native-paper";
import { View, StyleSheet } from "react-native";
import debounce from "lodash.debounce";

import {
Polarity,
Expand Down Expand Up @@ -47,15 +48,28 @@ function chooseEditor(entity: Entity) {

function PropositionEditor({ entity }: { entity: Proposition }) {
const dispatch = useDispatch();
const [text, setText] = useState(entity.text);

const debouncedDispatch = useMemo(
() =>
debounce((text: string) => {
dispatch(updateProposition({ id: entity.id, updates: { text } }));
}, 150),
[dispatch, entity.id],
);

const handleTextChange = (newText: string) => {
setText(newText);
debouncedDispatch(newText);
};

return (
<View>
<TextInput
value={entity.text}
value={text}
multiline={true}
numberOfLines={1}
onChangeText={(text) =>
dispatch(updateProposition({ id: entity.id, updates: { text } }))
}
onChangeText={handleTextChange}
/>
</View>
);
Expand Down Expand Up @@ -84,22 +98,35 @@ function JustificationEditor({ entity }: { entity: Justification }) {

function MediaExcerptEditor({ entity }: { entity: MediaExcerpt }) {
const dispatch = useDispatch();
const [text, setText] = useState(entity.sourceInfo.name);

const debouncedDispatch = useMemo(
() =>
debounce((text: string) => {
dispatch(
updateMediaExerpt({
id: entity.id,
updates: { sourceInfo: { name: text } },
}),
);
}, 150),
[dispatch, entity.id],
);

const handleTextChange = (newText: string) => {
setText(newText);
debouncedDispatch(newText);
};

return (
<View>
<TextInput
label="Source Title"
placeholder="“The Title” The Publication (2024-10-01)"
value={entity.sourceInfo.name}
value={text}
multiline={true}
numberOfLines={1}
onChangeText={(text) =>
dispatch(
updateMediaExerpt({
id: entity.id,
updates: { sourceInfo: { name: text } },
}),
)
}
onChangeText={handleTextChange}
/>
<Surface style={styles.quotationContainer}>
<Text style={styles.quotationLabel}>Quotation</Text>
Expand Down
2 changes: 2 additions & 0 deletions packages/ui-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"classnames": "^2.3.2",
"cytoscape": "^3.30.4",
"cytoscape-elk": "^2.2.0",
"lodash.debounce": "^4.0.8",
"lodash.throttle": "^4.1.1",
"react": "^18.2.0",
"react-cytoscapejs": "^2.0.0",
Expand All @@ -36,6 +37,7 @@
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.1",
"@types/cytoscape": "^3.19.11",
"@types/lodash.debounce": "^4.0.9",
"@types/lodash.throttle": "^4.1.9",
"@types/react": "^18.2.15",
"babel-jest": "^29.7.0",
Expand Down
34 changes: 26 additions & 8 deletions packages/ui-common/src/cytoscape/reactNodes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import cytoscape, {
} from "cytoscape";
import ReactDOM from "react-dom/client";
import throttle from "lodash.throttle";
import debounce from "lodash.debounce";

import { sunflower } from "../colors";

Expand All @@ -23,6 +24,8 @@ interface ReactNodesOptions {
layoutOptions: LayoutOptions;
// The delay before reactNodes applies a layout when one is necessary.
layoutThrottleDelay?: number;
// The delay before rendering the JSX after the node data changes.
nodeDataRenderDelay?: number;
logger: Logger;
}

Expand All @@ -37,10 +40,14 @@ export interface ReactNodeOptions {
unselectedStyle?: Partial<CSSStyleDeclaration>;
}

const defaultOptions: Required<Pick<ReactNodesOptions, "layoutThrottleDelay">> =
{
layoutThrottleDelay: 100,
};
type PassthroughOptions = Pick<ReactNodesOptions, "nodeDataRenderDelay">;

const defaultOptions: Required<
Pick<ReactNodesOptions, "layoutThrottleDelay" | "nodeDataRenderDelay">
> = {
layoutThrottleDelay: 500,
nodeDataRenderDelay: 150,
};

const defaultReactNodeOptions: ReactNodeOptions = {
query: "node", // selector for nodes to apply HTML to
Expand All @@ -65,8 +72,14 @@ function reactNodes(this: cytoscape.Core, options: ReactNodesOptions) {
this.layout(options.layoutOptions).run();
}, options.layoutThrottleDelay ?? defaultOptions.layoutThrottleDelay);

const { nodeDataRenderDelay } = options;
options.nodes.forEach((nodeOptions) =>
makeReactNode(this, options.logger, nodeOptions, layout),
makeReactNode(
this,
options.logger,
{ ...nodeOptions, nodeDataRenderDelay },
layout,
),
);

applyWebkitLayoutWorkaround(this, options.layoutOptions);
Expand Down Expand Up @@ -111,7 +124,7 @@ function applyWebkitLayoutWorkaround(
function makeReactNode(
cy: cytoscape.Core,
logger: Logger,
options: ReactNodeOptions,
options: ReactNodeOptions & PassthroughOptions,
layout: () => void,
) {
options = Object.assign({}, defaultReactNodeOptions, options);
Expand Down Expand Up @@ -170,9 +183,14 @@ function makeReactNode(
Object.assign(htmlElement.style, options.unselectedStyle);
}
});
node.on("data", function renderReactNode() {

// Debounce the node data event handler to reduce Redux updates
const debouncedDataHandler = debounce(function renderReactNode() {
renderJsxElement(reactRoot);
});
}, options.nodeDataRenderDelay);

node.on("data", debouncedDataHandler);

if (options.syncClasses) {
node.on("style", syncNodeClasses);
}
Expand Down

0 comments on commit 8af366f

Please sign in to comment.