redux-toolkit: dispatch inside createAsyncThunk calls causes rendering twice
Hello everyone.
I found this problem in all my applications. I create an action and call this action inside createAsyncThunk and see twice rendering.
I have been using redux and had no such problems, but when I switched to Redux Toolkit, problems arose. I created this example for you, using only the redux toolkit, the initial application shell. A small application is not difficult to look at the code.
To view behavior Video
Debugging here Changing this reducer Calling this createAsyncThunk Change the value here
"@react-native-async-storage/async-storage": "^1.19.3",
"@react-navigation/bottom-tabs": "^6.5.9",
"@react-navigation/native": "^6.1.8",
"@react-navigation/native-stack": "^6.9.14",
"@reduxjs/toolkit": "^1.9.7",
"axios": "^1.5.1",
"lodash": "^4.17.21",
"react": "18.2.0",
"react-native": "0.72.5",
"react-native-gesture-handler": "^2.13.1",
"react-native-indicators": "^0.17.0",
"react-native-reanimated": "^3.5.4",
"react-native-safe-area-context": "^4.7.2",
"react-native-screens": "^3.25.0",
"react-native-svg": "^13.14.0",
"react-redux": "^8.1.3",
"redux-persist": "^6.0.0"
It doesn’t matter whether the persister is connected or not, the behavior is the same!
About this issue
- Original URL
- State: closed
- Created 9 months ago
- Comments: 27 (6 by maintainers)
I could reproduce the issue, but I still don’t agree that this has anything to do with
createAsyncThunk
.As you can see, the second rerender occurs after the
fulfilled
action, which should never be the case. But that said:cAT
doesn’t contain any code that can even cause a rerender, so it has to be something else.This seems to generally be caused by the specific order and timing of actions flying here. To me this looks like a bug either in
react-redux
, or inreact
itself, more specifically theuseSyncExternalStore
implementation used by React Native.I’ll see if I can spend some more time with this these days.
@z-hayk: I think you’re still kind of missing the point on the
createAsyncThunk
part. We’re saying you don’t need to manually dispatch actions inside ofcreateAsyncThunk
most of the time! And also, you don’t needcreateAsyncThunk
for this use case as far as we can see!I’m also not sure I understand what the actual problem is to start with. React rendering is not a problem in and of itself - rendering is how React does updates. That’s normal. There are times you may need to optimize the behavior, but “React renders twice” is not necessarily a “bug” to be fixed.
Where and how are you seeing “rendering twice”? Is there an actual bug that is occurring in your app because of this? Or are you just seeing a couple renders and assuming that this is bad?
Please read the Redux style guide, especially the parts about treating actions as events and avoiding to dispatch multiple actions in sequence.
https://redux.js.org/style-guide/#avoid-dispatching-many-actions-sequentially
Generally: there is no technical difference at all between dispatching in an asyncThunk or dispatching anywhere else in your application.
The difference is that an asyncThunk dispatches a pending and a fulfilled action automatically, so if you don’t need those, you don’t need an asyncThink, and if you need those, you should use those to handle most of your logic.
My first observation is that both
addFavorite
anddeleteFavorite
appear to a misuse ofcreateAsyncThunk
. Neither of them is doing any async work at all, and they’re just dispatching more Redux actions.createAsyncThunk
is specifically intended for the use case of “make an async request, and dispatch actions before and after so we can keep track of the request status and results”.I’ll also note that
addFavorite
is doing work to clone an existing user object and overwrite one field, then putting the entire new user object into the action. That’s an anti-pattern. Instead, we recommend doing that work in the reducer, which will significantly simplify the logic.