Skip to content

Commit 39b4500

Browse files
authored
Fix: Unnecessary Re-rendering (#61)
* clean up onlayout and resize * lazy load pod & MyMonaco * memo mymonaco & lazy eval pods in canvas
1 parent d829f8f commit 39b4500

File tree

4 files changed

+73
-106
lines changed

4 files changed

+73
-106
lines changed

ui/src/components/Canvas.tsx

Lines changed: 38 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,7 @@ const ScopeNode = memo<Props>(({ data, id, isConnectable }) => {
6868
const [frame] = React.useState({
6969
translate: [0, 0],
7070
});
71-
const setSelected = useStore(store, (state) => state.setSelected);
7271
const selected = useStore(store, (state) => state.selected);
73-
const { setNodes } = useReactFlow();
7472

7573
const onResize = useCallback(({ width, height, offx, offy }) => {
7674
const node = nodesMap.get(id);
@@ -186,6 +184,8 @@ const ScopeNode = memo<Props>(({ data, id, isConnectable }) => {
186184
);
187185
});
188186

187+
// FIXME: the resultblock is rendered every time the parent codeNode changes (e.g., dragging), we may set the result number as a state of a pod to memoize the resultblock.
188+
189189
function ResultBlock({ pod, id }) {
190190
const store = useContext(RepoContext);
191191
if (!store) throw new Error("Missing BearContext.Provider in the tree");
@@ -252,12 +252,8 @@ function ResultBlock({ pod, id }) {
252252
const CodeNode = memo<Props>(({ data, id, isConnectable }) => {
253253
const store = useContext(RepoContext);
254254
if (!store) throw new Error("Missing BearContext.Provider in the tree");
255-
const pod = useStore(store, (state) => state.pods[id]);
256-
const setPodContent = useStore(store, (state) => state.setPodContent);
257-
const updatePod = useStore(store, (state) => state.updatePod);
258-
const clearResults = useStore(store, (s) => s.clearResults);
255+
// const pod = useStore(store, (state) => state.pods[id]);
259256
const wsRun = useStore(store, (state) => state.wsRun);
260-
const nodesMap = useStore(store, (state) => state.ydoc.getMap<Node>("pods"));
261257
const ref = useRef(null);
262258
const [target, setTarget] = React.useState<any>(null);
263259
const [frame] = React.useState({
@@ -266,30 +262,25 @@ const CodeNode = memo<Props>(({ data, id, isConnectable }) => {
266262
// right, bottom
267263
const [layout, setLayout] = useState("right");
268264
const { setNodes } = useReactFlow();
269-
const selected = useStore(store, (state) => state.selected);
265+
// const selected = useStore(store, (state) => state.selected);
270266
const setSelected = useStore(store, (state) => state.setSelected);
267+
const getPod = useStore(store, (state) => state.getPod);
268+
const pod = getPod(id);
269+
270+
const showResult = useStore(
271+
store,
272+
(state) =>
273+
state.pods[id].running ||
274+
state.pods[id].result ||
275+
state.pods[id].error ||
276+
state.pods[id].stdout ||
277+
state.pods[id].stderr
278+
);
271279

272-
const onResize = useCallback(({ width, height, offx, offy }) => {
273-
const node = nodesMap.get(id);
274-
if (node) {
275-
node.style = { ...node.style, width, height };
276-
node.position.x += offx;
277-
node.position.y += offy;
278-
nodesMap.set(id, node);
279-
}
280-
}, []);
281-
const onLayout = useCallback(({ height }) => {
282-
const node = nodesMap.get(id);
283-
if (node) {
284-
node.style = { ...node.style, height };
285-
nodesMap.set(id, node);
286-
}
287-
}, []);
288-
289-
React.useEffect(() => {
280+
useEffect(() => {
290281
setTarget(ref.current);
291282
}, []);
292-
if (!pod) return <Box>ERROR</Box>;
283+
// if (!pod) return <Box>ERROR</Box>;
293284
return (
294285
<Box
295286
sx={{
@@ -387,24 +378,8 @@ const CodeNode = memo<Props>(({ data, id, isConnectable }) => {
387378
);
388379
}}
389380
>
390-
<MyMonaco
391-
value={pod.content || ""}
392-
id={pod.id}
393-
onChange={(value) => {
394-
setPodContent({ id: pod.id, content: value });
395-
}}
396-
lang={pod.lang || "javascript"}
397-
onRun={() => {
398-
clearResults(pod.id);
399-
wsRun(pod.id);
400-
}}
401-
onLayout={onLayout}
402-
/>
403-
{(pod.running ||
404-
pod.stdout ||
405-
pod.stderr ||
406-
pod.result ||
407-
pod.error) && (
381+
<MyMonaco id={id} gitvalue="" />
382+
{showResult && (
408383
<Box
409384
className="nowheel"
410385
sx={{
@@ -424,43 +399,6 @@ const CodeNode = memo<Props>(({ data, id, isConnectable }) => {
424399
</Box>
425400
)}
426401
</Box>
427-
{false && (
428-
<Moveable
429-
target={target}
430-
resizable={true}
431-
keepRatio={false}
432-
throttleResize={1}
433-
renderDirections={["e", "s", "se"]}
434-
edge={false}
435-
zoom={1}
436-
origin={true}
437-
padding={{ left: 0, top: 0, right: 0, bottom: 0 }}
438-
onResizeStart={(e) => {
439-
e.setOrigin(["%", "%"]);
440-
e.dragStart && e.dragStart.set(frame.translate);
441-
}}
442-
onResize={(e) => {
443-
const beforeTranslate = e.drag.beforeTranslate;
444-
frame.translate = beforeTranslate;
445-
e.target.style.width = `${e.width}px`;
446-
e.target.style.height = `${e.height}px`;
447-
e.target.style.transform = `translate(${beforeTranslate[0]}px, ${beforeTranslate[1]}px)`;
448-
onResize({
449-
width: e.width,
450-
height: e.height,
451-
offx: beforeTranslate[0],
452-
offy: beforeTranslate[1],
453-
});
454-
updatePod({
455-
id,
456-
data: {
457-
width: e.width,
458-
height: e.height,
459-
},
460-
});
461-
}}
462-
/>
463-
)}
464402
</Box>
465403
);
466404
});
@@ -484,35 +422,39 @@ export function Canvas() {
484422
const store = useContext(RepoContext);
485423
if (!store) throw new Error("Missing BearContext.Provider in the tree");
486424
// the real pods
487-
const id2children = useStore(store, (state) => state.id2children);
488-
const pods = useStore(store, (state) => state.pods);
425+
const getId2children = useStore(store, (state) => state.getId2children);
426+
// const pods = useStore(store, (state) => state.pods);
427+
const getPod = useStore(store, (state) => state.getPod);
489428
const nodesMap = useStore(store, (state) => state.ydoc.getMap<Node>("pods"));
490429

491430
const getRealNodes = useCallback(
492431
(id, level) => {
493432
let res: any[] = [];
494-
let children = id2children[id];
433+
let children = getId2children(id) || [];
434+
console.log("getChildren", id, children);
435+
const pod = getPod(id);
495436
if (id !== "ROOT") {
496437
res.push({
497438
id: id,
498-
type: pods[id].type === "CODE" ? "code" : "scope",
439+
type: pod.type === "CODE" ? "code" : "scope",
499440
data: {
500441
// label: `ID: ${id}, parent: ${pods[id].parent}, pos: ${pods[id].x}, ${pods[id].y}`,
501442
label: id,
502443
},
503444
// position: { x: 100, y: 100 },
504-
position: { x: pods[id].x, y: pods[id].y },
505-
parentNode: pods[id].parent !== "ROOT" ? pods[id].parent : undefined,
445+
position: { x: pod.x, y: pod.y },
446+
parentNode: pod.parent !== "ROOT" ? pod.parent : undefined,
506447
extent: "parent",
507448
dragHandle: ".custom-drag-handle",
508449
level,
509450
style: {
510451
backgroundColor:
511-
pods[id].type === "CODE"
452+
pod.type === "CODE"
512453
? undefined
513454
: level2color[level] || level2color["default"],
514455
width: 700,
515-
height: pods[id].height,
456+
// for code node, don't set height, let it be auto
457+
height: pod.height || undefined,
516458
},
517459
});
518460
}
@@ -521,7 +463,7 @@ export function Canvas() {
521463
}
522464
return res;
523465
},
524-
[id2children, pods]
466+
[getId2children, getPod]
525467
);
526468
useEffect(() => {
527469
let nodes = getRealNodes("ROOT", -1);
@@ -593,7 +535,8 @@ export function Canvas() {
593535
} else {
594536
style = {
595537
width: 700,
596-
height: 300,
538+
// we must not set the height here, otherwise the auto layout will not work
539+
height: undefined,
597540
};
598541
}
599542

@@ -640,19 +583,19 @@ export function Canvas() {
640583
(node) => {
641584
let x = node.position.x;
642585
let y = node.position.y;
643-
let parent = pods[node.parent];
586+
let parent = getPod(node.parent);
644587
while (parent) {
645588
x += parent.x;
646589
y += parent.y;
647590
if (parent.parent) {
648-
parent = pods[parent.parent];
591+
parent = getPod(parent.parent);
649592
} else {
650593
break;
651594
}
652595
}
653596
return [x, y];
654597
},
655-
[pods]
598+
[getPod]
656599
);
657600

658601
const getScopeAt = useCallback(

ui/src/components/MyMonaco.tsx

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Position } from "monaco-editor";
2-
import { useState, useContext } from "react";
2+
import { useState, useContext, memo } from "react";
33
import MonacoEditor, { MonacoDiffEditor } from "react-monaco-editor";
44
import { monaco } from "react-monaco-editor";
55
import { useStore } from "zustand";
@@ -298,20 +298,31 @@ async function updateGitGutter(editor) {
298298
// not, and the instance will only be mounted once. All variables, even a object
299299
// like the pod object will be fixed at original state: changing pod.staged
300300
// won't be visible in the editorDidMount callback.
301-
export function MyMonaco({
302-
lang = "javascript",
303-
value = "",
301+
302+
interface MyMonacoProps {
303+
id: string;
304+
gitvalue: string;
305+
}
306+
307+
export const MyMonaco = memo<MyMonacoProps>(function MyMonaco({
304308
id = "0",
305309
gitvalue = null,
306-
onChange = (value) => {},
307-
onRun = () => {},
308-
onLayout = (height) => {},
309310
}) {
310-
// console.log("rendering monaco ..");
311311
// there's no racket language support
312312
const store = useContext(RepoContext);
313313
if (!store) throw new Error("Missing BearContext.Provider in the tree");
314314
const showLineNumbers = useStore(store, (state) => state.showLineNumbers);
315+
const getPod = useStore(store, (state) => state.getPod);
316+
const setPodContent = useStore(store, (state) => state.setPodContent);
317+
const clearResults = useStore(store, (s) => s.clearResults);
318+
const wsRun = useStore(store, (state) => state.wsRun);
319+
const value = getPod(id).content || "";
320+
let lang = getPod(id).lang || "javascript";
321+
const onChange = (value) => setPodContent({ id, content: value });
322+
const onRun = () => {
323+
clearResults(id);
324+
wsRun(id);
325+
};
315326

316327
if (lang === "racket") {
317328
lang = "scheme";
@@ -346,7 +357,7 @@ export function MyMonaco({
346357
// width: 800
347358
// editor.layout({ width: 800, height: contentHeight });
348359
editor.layout();
349-
onLayout(`${contentHeight}px`);
360+
// onLayout(`${contentHeight}px`);
350361
};
351362
editor.onDidContentSizeChange(updateHeight);
352363
// FIXME clean up?
@@ -420,4 +431,4 @@ export function MyMonaco({
420431
editorDidMount={onEditorDidMount}
421432
/>
422433
);
423-
}
434+
});

ui/src/lib/nodes.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,22 @@ export function useNodesStateSynced(nodeList) {
2929
if (!isNodeAddChange(change) && !isNodeResetChange(change)) {
3030
if (isNodeRemoveChange(change)) {
3131
nodesMap.delete(change.id);
32+
return;
3233
}
3334
const node = nextNodes.find((n) => n.id === change.id);
3435
if (!node) return;
3536

36-
if (change.type === "select") {
37+
if (change.type === "select" && change.selected) {
38+
// FIXME: consider the case where only unselect is called
3739
setSelected(node.id);
3840
return;
3941
}
4042

43+
if (change.type === "dimensions" && node.type === "code") {
44+
// the re-size event of codeNode don't need to be sync, just skip.
45+
return;
46+
}
47+
4148
if (node) {
4249
nodesMap.set(change.id, node);
4350
}

ui/src/lib/store.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ export interface RepoSlice {
185185
deleteClient: (clientId: any) => void;
186186
flipShowLineNumbers: () => void;
187187
disconnect: () => void;
188+
getPod: (string) => any;
189+
getPods: () => any;
190+
getId2children: (string) => string[];
188191
}
189192

190193
type BearState = RepoSlice & RuntimeSlice;
@@ -752,6 +755,9 @@ const createRepoSlice: StateCreator<
752755
state.ydoc.destroy();
753756
})
754757
),
758+
getPod: (id: string) => get().pods[id],
759+
getPods: () => get().pods,
760+
getId2children: (id: string) => get().id2children[id],
755761
});
756762

757763
export const createRepoStore = () =>

0 commit comments

Comments
 (0)