Ok, but hear me out, what if there were two ReactDOM renderers?
Why are we talking about this at all?
const doc = {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Here's some text"
}
]
}
]
}
Here's some text
Here's some text
Here's some text
Why is this so hard?
The answer seems maybe not so bad at first glance.
We just need to ask React to render first, and then hand the result to ProseMirror.
Let's try it!
const container = document.createElement('div');
const root = createRoot(container);
flushSync(() => {
root.render(<NodeViewWrapper { ... } />);
});
useLayoutEffect(() => {
...
const container = document.createElement('div');
const root = createRoot(container);
flushSync(() => {
root.render(<NodeViewWrapper { ... } />);
});
...
});
Warning: flushSync was called from inside a lifecycle method.
React cannot flush when React is already rendering. Consider
moving this call to a scheduler task or micro task.
🤦
react-reconciler
, that is
responsible for producing the virtual document.
Renderers implement methods like
createTextInstance()
and
appendChildToContainer()
.
createTextInstance(
text,
rootContainerInstance,
hostContext,
internalInstanceHandle,
) {
return document.createTextNode(text);
},
appendChildToContainer(container, child) {
container.appendChild(child);
}
import { createContainer, updateContainer } from 'react-reconciler';
class Surface extends React.Component {
componentDidMount() {
const {height, width} = this.props;
this._surface = Mode.Surface(+width, +height, this._tagRef);
this._mountNode = createContainer(
this._surface,
LegacyRoot,
null,
false,
false,
'',
);
updateContainer(
this.props.children,
this._mountNode,
this
);
}
...
Turns out... yes??
// export const isPrimaryRenderer = true;
export const isPrimaryRenderer = false;
$ yarn build
.../
$ cp ./build/react-dom/react-dom.development.js \
../react-prosemirror/react-dom-secondary.development.js
// import { createRoot } from 'react-dom/client';
import { createRoot } from 'react-dom-secondary/client';
useLayoutEffect(() => {
...
const container = document.createElement('div');
const root = createRoot(container);
flushSync(() => {
root.render(<NodeViewWrapper { ... } />);
});
...
});
🎉