react-redux icon indicating copy to clipboard operation
react-redux copied to clipboard

Dispatching at the same time as setState causes two renderings

Open mineoka-kento opened this issue 2 years ago • 9 comments

What version of React, ReactDOM/React Native, Redux, and React Redux are you using?

  • React: 18.0.0
  • ReactDOM:18.0.0
  • Redux Toolkit:1.8.1
  • React Redux:8.0.1

What is the current behavior?

Synchronous calls to dispatch and setState within useEffect will cause separate re-rendering.

Since it may not be possible to check visually, try using the Profiler in the React Developer Tools to see how it works.

https://codesandbox.io/s/brave-dew-cc7p4p?file=/src/features/counter/CounterEditor.tsx

screen-recording_1

What is the expected behavior?

The re-rendering occurs only once.

Which browser and OS are affected by this issue?

Chrome/100.0.4896.127, Windows10

Did this work in previous versions of React Redux?

  • [X] Yes

mineoka-kento avatar Apr 22 '22 05:04 mineoka-kento

Since the video was difficult to understand, I upload the original video file. screen-recording.zip

mineoka-kento avatar Apr 22 '22 05:04 mineoka-kento

Without having fully investigated yet: batching and render timing are owned by React, not React-Redux. We don't have full control over that.

I would have expected that all updates queued in a useEffect would be batched together, especially under React 18. But, the nuances of this one are tricky.

Either way I don't think there's anything we can specifically do here, and I don't think this is actually a "bug".

markerikson avatar Apr 22 '22 14:04 markerikson

The OP has used React.StrictMode , so I have suspected that makes useEffect run twice.

But, unfortunately, I could made another repro for this issue, without StrictMode. This should run ok with react-redux@7 + react@18, but if you run this with react-redux@8 + react@18, it throws an error, Maximum update depth exceeded.

Actually, the example I have made can be fixed, if you use dependencies for the useEffect. But this example shows that if you call setState and dispatch, a rerender, with updates from dispatch and without updates from setState, can happen even if you call setState prior to the dispatch.

HelloWorld017 avatar Aug 11 '22 06:08 HelloWorld017

Yes, it's actually that. Good call!

timdorr avatar Aug 12 '22 03:08 timdorr

Isn't that still a problem that we should maybe bring up with the React team? I mean, that problem essentially occurs because state updates happen out of order - the later Redux update happens before the earlier state setter call. That essentially means that Redux updates break out of the automated batching here, which was not a problem before when react-redux used setState to trigger a rerender.

phryneas avatar Aug 14 '22 15:08 phryneas

Andarist pointed out this is likely the same thing as https://github.com/facebook/react/issues/24831

markerikson avatar Aug 14 '22 17:08 markerikson

It seems like problem is more complex than just rendering twice.

Dispatching redux action in the same effect where a local state is updated cancels the local state update.

I've created a codesandbox with a simple to reproduce example.

https://codesandbox.io/s/friendly-worker-fdyvqk

Probably it's on React side as mentioned but posting here maybe it will be helpful.

MrBr avatar Oct 02 '22 20:10 MrBr

This will be changed on the React side - unfortunately at this point there's not really anything we can do about it: https://github.com/facebook/react/issues/25191#issuecomment-1244805920

phryneas avatar Oct 02 '22 21:10 phryneas