Duplicated React module import when Node.js imports 'react-dom/server', but the renderToString function resolves the React library via an import map #122
The browser import map works (i.e. looking at the client HTML the page loads and )
The node import does not work without manipulation. I get a React error when following the node imports:
Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
TypeError: Cannot read properties of null (reading 'useState')
at useState (https://ga.jspm.io/npm:react@18.0.0/dev.index.js:189:5711)
at Counter (file:///home/zach/code/saeon/repositories/somisana/web/.cache/ssr.algoa-bay-forecast.js:84:30)
at renderWithHooks (/home/zach/code/saeon/repositories/somisana/web/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5461:16)
at renderIndeterminateComponent (/home/zach/code/saeon/repositories/somisana/web/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5534:15)
at renderElement (/home/zach/code/saeon/repositories/somisana/web/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5749:7)
at renderNodeDestructive (/home/zach/code/saeon/repositories/somisana/web/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5888:11)
at retryTask (/home/zach/code/saeon/repositories/somisana/web/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6260:5)
at performWork (/home/zach/code/saeon/repositories/somisana/web/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6307:7)
at /home/zach/code/saeon/repositories/somisana/web/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6628:12
at scheduleWork (/home/zach/code/saeon/repositories/somisana/web/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:82:3)
I can solve the React error in three ways:
I can manually remove the top level react specifier in the node import map to get the following import map:
I can include an import to react-dom/server on the client (and console.log the import so that the code is used), and as a result I get additional imports in both the browser and the node import map
After manually deleting the node:buffer, node:process and node:stream specifiers then the code works as it should
Instead of including the react-dom/server import in the client code (I'm generating the node and browser import maps from the client entry point), I can specify an HTTP import in the SSR code:
# import { renderToString } from 'react-dom/server' # instead of this
import {renderToString} from 'https://ga.jspm.io/npm:react-dom@18.0.0/dev.server.node.js' # do this
This works, although I would have to manually update the import when package.json versions of react and react-dom change. It seems like a better solution would be to define an import map for the Node app in addition to the client app, merge them, and use the compiled node import map as the import resolver. Is this possible?
Alternatively
Would it be possible for the module to be cached by a hash of the code rather than the import location (seems like that is what's happening - sorry if that doesn't make sense, I'm not very familiar with how the import module cache works except that I can see that it exists)?
Koa.js SSR rendering
This is my SSR logic (Koa.js )
import { renderToString } from 'react-dom/server' // I suspect that this is importing React separately from the import map
...
export default async ctx => {
...
const { default : Component} = await import('./some/component/index.js')
ctx.set('Content-type', 'text/html')
ctx.body = html.replace(
'<div id="root"></div>',
`<div id="root">${renderToString(Component())}</div>`
) // The 'renderToString' function imports a different copy of React to what is in the module cache
}
Using JSPM I generate the following import maps.
Browser import map
Node.js (without module flag)
Node.js import map (with module flag)
I can solve the React error in three ways:
react
specifier in the node import map to get the following import map:react-dom/server
on the client (and console.log the import so that the code is used), and as a result I get additional imports in both the browser and the node import mapnew browser import map
new node import map
After manually deleting the
node:buffer
,node:process
andnode:stream
specifiers then the code works as it shouldreact-dom/server
import in the client code (I'm generating the node and browser import maps from the client entry point), I can specify an HTTP import in the SSR code:This works, although I would have to manually update the import when package.json versions of react and react-dom change. It seems like a better solution would be to define an import map for the Node app in addition to the client app, merge them, and use the compiled node import map as the import resolver. Is this possible?
Alternatively Would it be possible for the module to be cached by a hash of the code rather than the import location (seems like that is what's happening - sorry if that doesn't make sense, I'm not very familiar with how the import module cache works except that I can see that it exists)?
Koa.js SSR rendering This is my SSR logic (Koa.js )