Skip to content

Commit

Permalink
feat: SSR support (#637)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomas-c authored and prevwong committed May 17, 2024
1 parent 1212856 commit cb45f2c
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changeset/real-glasses-cross.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@craftjs/core': patch
---

SSR support
10 changes: 4 additions & 6 deletions packages/core/src/nodes/Element.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ERROR_TOP_LEVEL_ELEMENT_NO_ID, useEffectOnce } from '@craftjs/utils';
import { ERROR_TOP_LEVEL_ELEMENT_NO_ID } from '@craftjs/utils';
import React, { useState } from 'react';
import invariant from 'tiny-invariant';

Expand Down Expand Up @@ -47,9 +47,7 @@ export function Element<T extends React.ElementType>({
},
}));

const [linkedNodeId, setLinkedNodeId] = useState<NodeId | null>(null);

useEffectOnce(() => {
const [linkedNodeId] = useState<NodeId | null>(() => {
invariant(!!id, ERROR_TOP_LEVEL_ELEMENT_NO_ID);
const { id: nodeId, data } = node;

Expand Down Expand Up @@ -77,9 +75,9 @@ export function Element<T extends React.ElementType>({
linkedNodeId = tree.rootNodeId;
actions.history.ignore().addLinkedNodeFromTree(tree, nodeId, id);
}

setLinkedNodeId(linkedNodeId);
return linkedNodeId;
}
return null;
});

return linkedNodeId ? <NodeElement id={linkedNodeId} /> : null;
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/nodes/tests/Element.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ describe('<Element />', () => {
});

it('should call query.parseReactElement()', () => {
expect(parseReactElement).toHaveBeenCalledWith(
const arg0 = parseReactElement.mock.calls[0][0];
const arg0WithoutOwner = { ...arg0, _owner: null };
expect(arg0WithoutOwner).toEqual(
<Element {...elementProps}>{children}</Element>
);
});
Expand Down
43 changes: 15 additions & 28 deletions packages/core/src/render/Frame.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { deprecationWarning, ROOT_NODE } from '@craftjs/utils';
import React, { useEffect, useRef } from 'react';
import React, { useRef } from 'react';

import { useInternalEditor } from '../editor/useInternalEditor';
import { SerializedNodes } from '../interfaces';
Expand Down Expand Up @@ -39,41 +39,28 @@ export const Frame: React.FC<React.PropsWithChildren<FrameProps>> = ({
});
}

const initialState = useRef({
initialChildren: children,
initialData: data || json,
});
const isLoaded = useRef(false);

const isInitialChildrenLoadedRef = useRef(false);

useEffect(() => {
const { initialChildren, initialData } = initialState.current;
if (!isLoaded.current) {
const initialData = data || json;

if (initialData) {
actions.history.ignore().deserialize(initialData);
return;
}
} else if (children) {
const rootNode = React.Children.only(children) as React.ReactElement;

// Prevent recreating Nodes from child elements if we already did it the first time
// Usually an issue in React Strict Mode where this hook is called twice which results in orphaned Nodes
const isInitialChildrenLoaded = isInitialChildrenLoadedRef.current;
const node = query.parseReactElement(rootNode).toNodeTree((node, jsx) => {
if (jsx === rootNode) {
node.id = ROOT_NODE;
}
return node;
});

if (!initialChildren || isInitialChildrenLoaded) {
return;
actions.history.ignore().addNodeTree(node);
}

const rootNode = React.Children.only(initialChildren) as React.ReactElement;

const node = query.parseReactElement(rootNode).toNodeTree((node, jsx) => {
if (jsx === rootNode) {
node.id = ROOT_NODE;
}
return node;
});

actions.history.ignore().addNodeTree(node);
isInitialChildrenLoadedRef.current = true;
}, [actions, query]);
isLoaded.current = true;
}

return <RenderRootNode />;
};

0 comments on commit cb45f2c

Please sign in to comment.