I have a react app that needs to render into two different DOM root elements. What is the best way that I can share data between them based on changes inside one of them? It seems like I cannot wrap two apps in a context provider or use a state/effect/callback way to have communication between them.
ReactDOM.render(
<ReactApp1 componentData={}/>
, rootElement1
);
ReactDOM.render(
<ReactApp2 componentData={}/>
, rootElement2
);
CodePudding user response:
If the only reason you're splitting things up is because you need to put content into two unrelated dom nodes, you may be able to achieve this with a portal instead of two calls to ReactDOM.render. That way, you can use standard props and/or context to pass things between your components.
ReactDOM.render(
<App />
rootElement1
)
const App = () => {
const rootElement2 = /* some code to select rootElement2 from the page. eg, document.getElementById('someId') */
return (
<>
<ReactApp1 componentData={/*...*/}/>
{React.createPortal(<ReactApp2 componentData={/*...*/} />, rootElement2)}
</>
)
}
If that's not possible and you need completely separate calls to ReactDOM.render, then most likely you will need to create some pub/sub code and have the apps send messages to eachother. If you have a favorite library for event emitters you can use that, or if you want to create your own it might look something like this:
// These lines will probably be in their own file
const subscriptions = [];
export const subscribe = (callback) => {
subscriptions.push(callback);
const unsubscribe = () => {
const i = subscriptions.findIndex(callback);
subscriptions.splice(i, 1);
}
return unsubscribe
}
export const publish = (data) => {
[...subscriptions].forEach(callback => callback(data));
}
// Used like:
const ReactApp1 = () => {
const [count, setCount] = useState(0);
useEffect(() => {
// Listen to messages
const unsubscribe = subscribe((data) => {
setCount(data);
});
return unsubscribe;
}, []);
return <div onClick={() => {
publish(count 1);
}}/>
}