Omnistac / zedux

:zap: A Molecular State Engine for React
https://Omnistac.github.io/zedux/
MIT License
330 stars 6 forks source link

Atom State breaks when the same component is used across multiple windows #100

Open ChrisBoon opened 2 months ago

ChrisBoon commented 2 months ago

Zedux Version

1.2.1. Also tested 1.2.0.

Description

When you use setInternals to work across windows there appears to be a limitation that the same component cannot be used in multiple windows. Eg if I have a WindowFrame wrapper component that accesses a theme atom via useAtomState or useAtomValue to update its theme and I open 3 different windows that contain this WindowFrame component, only one of them will receive live updates. If, instead, each window uses a unique react component and gets the theme from the atom separately, it works fine. This appears to be due to how useAtomInstance generates a key using const dependentKey = useReactComponentId(). This appears to make the keys non-unique across windows. One way to see this is likely the case is to see that the graph only seems to have the number of dependents that matches the maximum instance count in a given window.

This is causing us quite an issue as our app lets users pop out different parts of dashboards and we chose Zedux specifically because it has great multi-window support (it's great generally, but this was the deciding factor), but as we've begun standardizing and abstracting to generic components this issue has caused parts of our WIP app to stop live updating.

Below I've included a small reproduction using window.open. In our real app we use OpenFin, but the issue is the same either way. I guess ideally we'd be able to inject the window name or uuid into the dependentKey.

Reproduction

Please see this demo in StackBlitz (I had trouble getting windows to work well in codesandbox). Note that I disabled strict mode and that re-enabling it causes a slightly different behavior. I've tried to add notes in the comments and on the UI itself to explain how to trigger the issue, but am more than happy to try and make clearer.

https://stackblitz.com/edit/vitejs-vite-q37ggg?file=package.json

Thanks for any help or advice you can give on this.

bowheart commented 2 months ago

Hey @ChrisBoon! Thank you so much for taking the time to put together such a good reproduction! I was able to see the issue immediately. Your assessment is correct, useReactComponentId is not generating a unique id across windows.

useReactComponentId relies on React's useId for this. And React fortunately provides a way to control these per-app using the identifierPrefix option on react-dom's createRoot. For example:

ReactDOM.createRoot(document.getElementById('root'), {
  identifierPrefix: document.title, // or location.pathname or any uuid
}).render(
  <App />
);

Updated stackblitz example:

https://stackblitz.com/edit/vitejs-vite-swvdpt?file=src%2Fmain.jsx

cboon-jeff commented 2 months ago

That's awesome. My bad for not following it to its conclusion and reading the react docs on useId - haven't come across that one before. Thanks for another really quick response, really appreciate it.