Skip to content

Commit

Permalink
perf: Memoize child components (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
motform authored Feb 13, 2024
1 parent af878ff commit 0f42781
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 83 deletions.
2 changes: 1 addition & 1 deletion packages/djedi-json/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "djedi-json",
"version": "0.6.2",
"version": "0.6.3",
"main": "build/index.js",
"module": "build/index.esm.js",
"types": "build/index.d.ts",
Expand Down
31 changes: 17 additions & 14 deletions packages/djedi-json/src/Renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,33 @@ export interface RendererProps {
}

const Renderer: React.FC<RendererProps> = ({ config, tree }) => {
if (tree === undefined) {
return null;
}
if (tree == null) return null;

const { components } = config;

// find the config for this component
const Config = components.find(c => c.type === tree.type);
/** Find the config for this component. */
const Config = React.useMemo(() => {
return components.find(({ type }) => type === tree.type);
}, [components, tree.type]);

// No type found. This is not a component.
if (!Config) {
return null;
}
if (!Config) return null;

const { children, ...props } = tree.content;

const renderedChildren = React.useMemo(() => {
if (Array.isArray(children)) {
return children.map((t: NodeTreeItem, i: number) => (
<Renderer key={i} tree={t} config={config} />
));
} else return children;
}, [children, config]);

return (
<Config.Component {...props} data-uri={tree.uri}>
{Array.isArray(children)
? children.map((t: NodeTreeItem, i: number) => (
<Renderer key={i} tree={t} config={config} />
))
: children}
{renderedChildren}
</Config.Component>
);
};

export default Renderer;
export default React.memo(Renderer);
41 changes: 21 additions & 20 deletions packages/djedi-json/src/core/EditGroup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const EditGroup: React.FC<EditGroupProps> = ({ content }) => {
const { patch, editing, tree, setEdit } = useEdit();

const c: ComponentConfig | undefined = React.useMemo(
() => config.components.find(t => t.type === tree.type),
() => config.components.find(({ type }) => type === tree.type),
[config, tree]
);

Expand All @@ -30,30 +30,31 @@ const EditGroup: React.FC<EditGroupProps> = ({ content }) => {

const handleSetEdit = React.useCallback((edit: boolean) => () => setEdit(edit), [setEdit]);

const renderedComponents = React.useMemo(() => {
return Object.entries(content).map(([k, editConfig]) => {
const { type, ...editProps } = editConfig;
const { Component } = config.edit[type];

return (
<section className={styles.layout} key={k}>
<Component
label={k}
value={tree?.content[k] ?? undefined}
{...editProps}
onChange={handleChange(k)}
/>
</section>
);
});
}, [content, tree, handleChange, config.edit]);

return editing ? (
<Modal onClose={handleSetEdit(false)}>
<div className={styles.separate}>
<h2 className={styles.title}>{c?.title ?? ''}</h2>
<p>{c?.description ?? ''}</p>

{Object.entries(content).map(([k, editConfig]) => {
// for now; opt out of displaying children as a prop here.
if (k === 'children') return null;

const { type, ...editProps } = editConfig;
const { Component } = config.edit[type];

return (
<section className={styles.layout} key={k}>
<Component
label={k}
value={tree?.content[k] ?? undefined}
{...editProps}
onChange={handleChange(k)}
/>
</section>
);
})}
{renderedComponents}

<Button onClick={handleSetEdit(false)} className={styles.close}>
<CloseSVG width="24px" fill="currentColor" />
Expand All @@ -63,4 +64,4 @@ const EditGroup: React.FC<EditGroupProps> = ({ content }) => {
) : null;
};

export default EditGroup;
export default React.memo(EditGroup);
1 change: 1 addition & 0 deletions packages/djedi-json/src/core/Editable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ const Editable: React.FC<{
</span>
)}
</span>

{editing && <EditGroup content={configContent} />}
</>
)}
Expand Down
27 changes: 13 additions & 14 deletions packages/djedi-json/src/core/Renderer/EditorTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,27 @@ const EditorTree: React.FC<{ tree: NodeTreeItem; path?: string[]; children?: Rea

// find the config for this component
const Config = React.useMemo(() => {
return components.find(c => c.type === tree.type);
return components.find(({ type }) => type === tree.type);
}, [components, tree]);

if (!Config) {
return null;
}
if (!Config) return null;

const { children } = tree.content;

const renderedChildren = React.useMemo(() => {
if (Array.isArray(children)) {
return children?.map((child, i) => {
const childPath = [...path, 'content', 'children', String(i)];
return <EditorTree tree={child} key={child.__ref} path={childPath} />;
});
}
}, [children, path]);

return (
<Editable config={Config} tree={tree} path={path}>
{Array.isArray(children) &&
children?.map((child, i) => {
const childPath = [...path, 'content', 'children', i.toString()];
return <EditorTree tree={child} key={child.__ref} path={childPath} />;
})}
{renderedChildren}
</Editable>
);

// This tree has no children. Render as is.

// return <Editable config={Config} data={item.content} key={index} path={path} index={index} />;
};

export default EditorTree;
export default React.memo(EditorTree);
28 changes: 0 additions & 28 deletions packages/djedi-json/src/core/Renderer/IsolateStyles.tsx

This file was deleted.

6 changes: 2 additions & 4 deletions packages/djedi-json/src/core/Renderer/Preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import React from 'react';

import { useCMS } from '../../contexts/cms';
import EditorTree from './EditorTree';
import IsolateStyles from './IsolateStyles';

const Preview: React.FC = () => {
const { tree } = useCMS();

return <IsolateStyles>{tree && <EditorTree tree={tree} />}</IsolateStyles>;
return tree ? <EditorTree tree={tree} /> : null;
};

export default Preview;
export default React.memo(Preview);
4 changes: 2 additions & 2 deletions packages/djedi-json/src/core/Tree/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ export const reducer = (state: NodeTreeItem, action: TreeReducerAction) => {
const to = [...action.to];

// to and from become the path of their respective parent nodes
const fromIndex = parseInt(from.pop());
const toIndex = parseInt(to.pop());
const fromIndex = Number.parseInt(from.pop());
const toIndex = Number.parseInt(to.pop());

// `fromParent` and `toParent` should reference the same array
// if the element is only moved within an array, otherwise the
Expand Down

0 comments on commit 0f42781

Please sign in to comment.