Bug: re-ordering components with stable keys invalidates refs/state, since 19.0.0
I've tested with 18.3.1, 19.0.0, and 19.1.1.
I build a modular synth application which uses the Web Audio API. Since it's possible for synth patches to grow quite large, I memoize the ordered list of modules for the sake of having a sensible keyboard navigation order. This means that the order of rendered modules can change.
Up until React 18.3.1, this changing order of rendered modules was not causing any issues. I make sure to use stable unique identifiers as keys when rendering, so components' internal state remains stable as the modules are dragged around the canvas.
Here's a snippet of the code used to render the modules:
const sortedModules = useMemo(
() => {
// ... order the modules based on their positions in the canvas
}),
[state.modulePositions, state.modules],
);
return (
<>
{sortedModules.map(([module, position]) => (
<Module
key={module.moduleKey}
module={module}
position={position}
state={state}
dispatch={dispatch}
/>
))}
</>
);
Internally, <Module /> calls a hook which stores a ref to a "node" object – which acts as its handle into the audio API – and initializes it like so:
// ...
const nodeRef = useRef<NodeType>(undefined);
if (!nodeRef.current) {
nodeRef.current = nodeFactory();
}
// ...
So, since React 19.0.0, this ref seems to get out of sync when the order of the rendered components changes.
React version: 19.0.0, 19.1.1
Steps To Reproduce
-
git clone https://github.com/rain-sk/synth.kitchen.git && cd synth.kitchen -
git checkout demo-react19-issues -
cd app/web -
npm i && npm run dev - Visit localhost:8080 and press "start"
- Double-click in the center of the screen, then choose one of the options in the menu to add a new module to the canvas
- Drag the output module to the bottom-right corner of the screen, past the newly-added module
Expected:
- internal module state is stable regardless of render order
Result:
- hit the debugger line in the
useNodehook, indicating that the ref was reset, even though we've already initialized the corresponding node
Note: the commit behind the HEAD of demo-react19-issues is the one which adds the debugger statement. Switching to that commit (b8f76f9f845328a6e83fdd3a13981f82cf16fe11) and re-installing node_modules, demonstrates that this problem did not exist prior to React 19.
Link to code example:
The current behavior
Changing render order of components with stable keys results in broken internal refs.
The expected behavior
Internal refs of react components are stable regardless of render order, given stable key props.
As a note, I tried switching from useRef to useState for storing the node, but this leads to a whole other kind of crash caused by an InvalidAccessException. Triggering this crash requires manually re-ordering the modules three times.
Applying the same ref->state change with react and react-dom at 18.3.1, there is no crash.
This seems to be a duplicate of #32561, or at least they seem related.
My opinion: the changes that came with <StrictMode> with React 18 have been helpful in finding certain kinds of bugs, but when working with the Web Audio API, it makes React a pain to use as a framework.
Can you reproduce this with a production build as well? We have a similar issue that only happens in dev with StrictMode: https://github.com/facebook/react/issues/32561
@eps1lon I cannot reproduce in production. This is only in dev with StrictMode.
This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!
I have updated the example branch mentioned in the bug summary to test with React 19.2.0 and the issue persists.