diff options
Diffstat (limited to 'packages/integrations/react')
-rw-r--r-- | packages/integrations/react/client.js | 7 | ||||
-rw-r--r-- | packages/integrations/react/context.js | 24 | ||||
-rw-r--r-- | packages/integrations/react/server.js | 31 |
3 files changed, 50 insertions, 12 deletions
diff --git a/packages/integrations/react/client.js b/packages/integrations/react/client.js index 3807ab410..366d499e3 100644 --- a/packages/integrations/react/client.js +++ b/packages/integrations/react/client.js @@ -13,6 +13,9 @@ function isAlreadyHydrated(element) { export default (element) => (Component, props, { default: children, ...slotted }, { client }) => { if (!element.hasAttribute('ssr')) return; + const renderOptions = { + identifierPrefix: element.getAttribute('prefix') + } for (const [key, value] of Object.entries(slotted)) { props[key] = createElement(StaticHtml, { value, name: key }); } @@ -28,10 +31,10 @@ export default (element) => } if (client === 'only') { return startTransition(() => { - createRoot(element).render(componentEl); + createRoot(element, renderOptions).render(componentEl); }); } return startTransition(() => { - hydrateRoot(element, componentEl); + hydrateRoot(element, componentEl, renderOptions); }); }; diff --git a/packages/integrations/react/context.js b/packages/integrations/react/context.js new file mode 100644 index 000000000..5d9b1d7b1 --- /dev/null +++ b/packages/integrations/react/context.js @@ -0,0 +1,24 @@ +const contexts = new WeakMap(); + +const ID_PREFIX = 'r'; + +function getContext(rendererContextResult) { + if (contexts.has(rendererContextResult)) { + return contexts.get(rendererContextResult); + } + const ctx = { + currentIndex: 0, + get id() { + return ID_PREFIX + this.currentIndex.toString(); + }, + }; + contexts.set(rendererContextResult, ctx); + return ctx; +} + +export function incrementId(rendererContextResult) { + const ctx = getContext(rendererContextResult) + const id = ctx.id; + ctx.currentIndex++; + return id; +} diff --git a/packages/integrations/react/server.js b/packages/integrations/react/server.js index 01a135a9b..0d85984f9 100644 --- a/packages/integrations/react/server.js +++ b/packages/integrations/react/server.js @@ -1,6 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom/server'; import StaticHtml from './static-html.js'; +import { incrementId } from './context.js'; const slotName = (str) => str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase()); const reactTypeof = Symbol.for('react.element'); @@ -58,6 +59,12 @@ async function getNodeWritable() { } async function renderToStaticMarkup(Component, props, { default: children, ...slotted }, metadata) { + let prefix; + if (this && this.result) { + prefix = incrementId(this.result) + } + const attrs = { prefix }; + delete props['class']; const slots = {}; for (const [key, value] of Object.entries(slotted)) { @@ -74,29 +81,33 @@ async function renderToStaticMarkup(Component, props, { default: children, ...sl newProps.children = React.createElement(StaticHtml, { value: newChildren }); } const vnode = React.createElement(Component, newProps); + const renderOptions = { + identifierPrefix: prefix + } let html; if (metadata && metadata.hydrate) { if ('renderToReadableStream' in ReactDOM) { - html = await renderToReadableStreamAsync(vnode); + html = await renderToReadableStreamAsync(vnode, renderOptions); } else { - html = await renderToPipeableStreamAsync(vnode); + html = await renderToPipeableStreamAsync(vnode, renderOptions); } } else { if ('renderToReadableStream' in ReactDOM) { - html = await renderToReadableStreamAsync(vnode); + html = await renderToReadableStreamAsync(vnode, renderOptions); } else { - html = await renderToStaticNodeStreamAsync(vnode); + html = await renderToStaticNodeStreamAsync(vnode, renderOptions); } } - return { html }; + return { html, attrs }; } -async function renderToPipeableStreamAsync(vnode) { +async function renderToPipeableStreamAsync(vnode, options) { const Writable = await getNodeWritable(); let html = ''; return new Promise((resolve, reject) => { let error = undefined; let stream = ReactDOM.renderToPipeableStream(vnode, { + ...options, onError(err) { error = err; reject(error); @@ -118,11 +129,11 @@ async function renderToPipeableStreamAsync(vnode) { }); } -async function renderToStaticNodeStreamAsync(vnode) { +async function renderToStaticNodeStreamAsync(vnode, options) { const Writable = await getNodeWritable(); let html = ''; return new Promise((resolve, reject) => { - let stream = ReactDOM.renderToStaticNodeStream(vnode); + let stream = ReactDOM.renderToStaticNodeStream(vnode, options); stream.on('error', (err) => { reject(err); }); @@ -164,8 +175,8 @@ async function readResult(stream) { } } -async function renderToReadableStreamAsync(vnode) { - return await readResult(await ReactDOM.renderToReadableStream(vnode)); +async function renderToReadableStreamAsync(vnode, options) { + return await readResult(await ReactDOM.renderToReadableStream(vnode, options)); } export default { |