From f10e9bd4928927aff294ea635d9231b960dc482a Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Wed, 10 Jul 2024 17:50:50 +0200 Subject: [PATCH] fix(react): hydrate react components on append/prepend/before/after --- .changeset/moody-turtles-retire.md | 6 ++ packages/actions/src/actions.ts | 10 +++ .../react/src/actions-react-plugin.test.tsx | 74 +++++++++++++++++++ packages/react/src/root.ts | 7 +- 4 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 .changeset/moody-turtles-retire.md diff --git a/.changeset/moody-turtles-retire.md b/.changeset/moody-turtles-retire.md new file mode 100644 index 0000000..2ae9548 --- /dev/null +++ b/.changeset/moody-turtles-retire.md @@ -0,0 +1,6 @@ +--- +'@coldwired/actions': patch +'@coldwired/react': patch +--- + +fix some actions diff --git a/packages/actions/src/actions.ts b/packages/actions/src/actions.ts index 91f6bd8..5d63431 100644 --- a/packages/actions/src/actions.ts +++ b/packages/actions/src/actions.ts @@ -345,18 +345,27 @@ export class Actions { private _after({ targets, fragment }: Pick) { for (const element of targets) { element.after(getDocumentFragment(fragment, element)); + const parent = element.parentElement; + if (parent) { + this.plugins.forEach((plugin) => plugin.onCreateElement?.(parent)); + } } } private _before({ targets, fragment }: Pick) { for (const element of targets) { element.before(getDocumentFragment(fragment, element)); + const parent = element.parentElement; + if (parent) { + this.plugins.forEach((plugin) => plugin.onCreateElement?.(parent)); + } } } private _append({ targets, fragment }: Pick) { for (const element of targets) { element.append(getDocumentFragment(fragment, element, true)); + this.plugins.forEach((plugin) => plugin.onCreateElement?.(element)); } } @@ -366,6 +375,7 @@ export class Actions { }: Pick) { for (const element of targets) { element.prepend(getDocumentFragment(fragment, element, true)); + this.plugins.forEach((plugin) => plugin.onCreateElement?.(element)); } } diff --git a/packages/react/src/actions-react-plugin.test.tsx b/packages/react/src/actions-react-plugin.test.tsx index 5c843aa..2592b5b 100644 --- a/packages/react/src/actions-react-plugin.test.tsx +++ b/packages/react/src/actions-react-plugin.test.tsx @@ -223,5 +223,79 @@ describe('@coldwired/react', () => { ); expect(root.getCache().size).toEqual(1); }); + + actions.append({ + targets: 'section', + fragment: `
toto <${DEFAULT_TAG_NAME}><${REACT_COMPONENT_TAG} ${NAME_ATTRIBUTE}="Counter" ${PROPS_ATTRIBUTE}="${encodeProps({ label: 'One more Count' })}">
`, + }); + await actions.ready(); + await waitFor(() => { + expect(document.body.innerHTML).toEqual( + layout( + `
<${DEFAULT_TAG_NAME} id="frag-1">

My New Count: 3

toto <${DEFAULT_TAG_NAME}>

One more Count: 0

`, + ), + ); + expect(root.getCache().size).toEqual(2); + }); + + actions.update({ + targets: 'section', + fragment: '

hello

', + }); + await actions.ready(); + + actions.prepend({ + targets: 'section', + fragment: `<${DEFAULT_TAG_NAME}><${REACT_COMPONENT_TAG} ${NAME_ATTRIBUTE}="Counter" ${PROPS_ATTRIBUTE}="${encodeProps({ label: 'One more Count' })}">`, + }); + await actions.ready(); + await waitFor(() => { + expect(document.body.innerHTML).toEqual( + layout( + `
<${DEFAULT_TAG_NAME}>

One more Count: 0

hello

`, + ), + ); + expect(root.getCache().size).toEqual(1); + }); + + actions.update({ + targets: 'section', + fragment: '

hello

', + }); + await actions.ready(); + + actions.after({ + targets: 'p', + fragment: `<${DEFAULT_TAG_NAME}><${REACT_COMPONENT_TAG} ${NAME_ATTRIBUTE}="Counter" ${PROPS_ATTRIBUTE}="${encodeProps({ label: 'One more Count' })}">`, + }); + await actions.ready(); + await waitFor(() => { + expect(document.body.innerHTML).toEqual( + layout( + `

hello

<${DEFAULT_TAG_NAME}>

One more Count: 0

`, + ), + ); + expect(root.getCache().size).toEqual(1); + }); + + actions.update({ + targets: 'section', + fragment: '

hello

', + }); + await actions.ready(); + + actions.before({ + targets: 'p', + fragment: `<${DEFAULT_TAG_NAME}><${REACT_COMPONENT_TAG} ${NAME_ATTRIBUTE}="Counter" ${PROPS_ATTRIBUTE}="${encodeProps({ label: 'One more Count' })}">`, + }); + await actions.ready(); + await waitFor(() => { + expect(document.body.innerHTML).toEqual( + layout( + `
<${DEFAULT_TAG_NAME}>

One more Count: 0

hello

`, + ), + ); + expect(root.getCache().size).toEqual(1); + }); }); }); diff --git a/packages/react/src/root.ts b/packages/react/src/root.ts index 41c056d..520b1e9 100644 --- a/packages/react/src/root.ts +++ b/packages/react/src/root.ts @@ -90,6 +90,12 @@ export function createRoot( const notify = () => { if (!isDestroyed) { + cache.forEach((_, element) => { + if (!element.isConnected) { + cache.delete(element); + mounted.delete(element); + } + }); cache = new Map(cache); subscriptions.forEach((callback) => callback()); } @@ -107,7 +113,6 @@ export function createRoot( if (isElement(fragment) && fragment.tagName.toLowerCase() != schema.fragmentTagName) { throw new Error('Cannot rerender with a non-fragment element'); } - //const { hydrate } = await import('./react-tree-builder'); await preload(fragment, (names) => manifestLoader(names, loader, manifest), schema); const tree = hydrate(fragment, manifest, schema); if (reset) {