react-three-fiber
react-three-fiber copied to clipboard
createPortal()'s state merging uses stale data
Hi there,
when using createPortal(children, scene, { camera })
with a custom state override (e.g. for the camera), the state gets reverted back to its initial value, when the parent-store updates. This also happens for any other state override you provide there.
Here is a sandbox with a minimal reproduction. There the camera within the portal should not change, when the parent store is updated.
This bug is only visible, when the createPortal
component is not also immeditaly re-rendered by the store update. In that case, the re-render overwrites the state again with the correct values.
The root-cause seems to lie in this effect:
React.useEffect(() => {
// Subscribe to previous root-state and copy changes over to the mirrored portal-state
const unsub = previousRoot.subscribe((prev) => usePortalStore.setState((state) => inject(prev, state)))
return () => {
unsub()
usePortalStore.destroy()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
It is missing a dependency on the inject
function, which means that it always uses the initial state data to do the state merge.
If I understand it right, I think the hook needs to be split in two:
React.useEffect(() => {
// Subscribe to previous root-state and copy changes over to the mirrored portal-state
const unsub = previousRoot.subscribe((prev) => usePortalStore.setState((state) => inject(prev, state)))
return () => {
unsub()
}
}, [inject])
React.useEffect(() => {
return () => {
usePortalStore.destroy()
}
}, [])