Skip to content

Bug: useSyncExternalStore does not update internal value if a setState is also called, outside useEffect, causing store sets not re-render #25565

Closed
@pandaiolo

Description

@pandaiolo

In a custom hook, I forgot to wrap my setState call in a useEffect, which did not pose any problems prior to React 18 but now this does not work anymore. The component does not re-render if the store is changed back to its initial value. I guess this might be expected, but there was no warning whatsoever, so it was hard to find out.

So this is more like a question:

  • Is that behavior a bug?
  • If it is not, would it be useful to have a warning for another developer making the same mistake?

React version: 18

Steps To Reproduce

  1. Have a component use useSyncExternalStore (for example redux 8)
  2. Call setState outside of useEffect or event handlers (in render)
  3. Trigger a change to the store back and forth

Link to code example: https://codesandbox.io/s/react-18-redux-8-reproduce-bug-ojdx43

The current behavior

  • The component does re-render if the store value is updated to something different than its initial value
  • The component does not re-render if the store is updated to its initial value again

The expected behavior

  • The component does re-render if the store value is updated to something different than its initial value
  • The component also does re-render if the store is updated to its initial value again

Further digging

When I tried to trace down the issue, I found that the setState called outside useEffect would mess up with the fiber update queue, in particular the prior pushEffect supposedly calling updateStoreInstance with the latest value to have the internal cached instance up to date. It does not get called at all (cancelled? overridden?) because of the unwrapped setState.

As a consequence, that cached instance never gets updated. Any other value but the initial value hence correctly triggers a re-render, as the checkIfSnapshotChanged ends up always comparing against that initial value.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Status: UnconfirmedA potential issue that we haven't yet confirmed as a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions