reduxjs / react-redux

Official React bindings for Redux
https://react-redux.js.org
MIT License
23.34k stars 3.37k forks source link

Dispatching at the same time as setState causes two renderings #1912

Open mineoka-kento opened 2 years ago

mineoka-kento commented 2 years ago

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

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?

mineoka-kento commented 2 years ago

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

markerikson commented 2 years ago

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".

HelloWorld017 commented 2 years ago

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.

timdorr commented 2 years ago

Yes, it's actually that. Good call!

phryneas commented 2 years ago

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.

markerikson commented 2 years ago

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

MrBr commented 1 year ago

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.

phryneas commented 1 year ago

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

markerikson commented 1 month ago

Update: this is hopefully fixed in React 19: