reduxjs / redux-toolkit

The official, opinionated, batteries-included toolset for efficient Redux development
https://redux-toolkit.js.org
MIT License
10.76k stars 1.18k forks source link

[Bug?] app-index.js:33 Warning: Prop 'disabled' did not match. Server: "" Client: "false" (with suspense in Next.js) #4511

Closed Enkratia closed 4 months ago

Enkratia commented 4 months ago

When i use Redux-toolkit with<Suspense> in Next.js project i am getting an error: app-index.js:33 Warning: Prop 'disabled' did not match. Server: "" Client: "false"

I tried same thing with native react context - no errors.

Link to reproduction (with comments on how to launch the project): https://github.com/Enkratia/redux-suspense-issue

phryneas commented 4 months ago

Your context and Redux implementations return completely different values, so I don't think they can be compared: In your context, your useSetCart function will return undefined if the useEffect has not been executed, and in your Redux implementation, your selector will return initialState.

That said, this is not a "suspense bug with Redux", but a bug you introduce with a Suspense boundary: you tell React to render only half of your application, and the other half later.

This changes the timing of your application massively, and probably leads to your error.

Enkratia commented 4 months ago

@phryneas thank you very much for response, but it's hard to believe in the idea: "you introduced the bug", not a "suspense bug with Redux". Basically i put state in one component and read the state in another component. That is why react context and redux tookit is needed. Ofcourse, you are the co-maintainer of this library, so there is no much space for disagreement.

Nonthehless it would be cool if you could write solution for the bug that i introduced, how to do it right with redux toolkit. Anyway this issue can be closed, no more questions.

phryneas commented 4 months ago

I have to be honest here - your code is very convoluted, and I'm not even sure what you want to do in the first place, so I can also not suggest how to fix it.

I do think that you have some wrong understanding of what is going on in React here. Your code renders in three different places at three slightly overlapping different times: on your RSC server, on your SSR server and in the browser. It seems to be reliant on things that can only happen in the browser (useEffect), but at the same time wait for thing happening on your RSC server (combining an async component with Suspense).

As a result, your SSR server ends up with components rendering in a different order than they do in the browser - especially since you seem to rely on timing guarantees that one subtree renders before another subtree, which is something that is just accidental and not guaranteed by React in the first place.

But all of that is not "a bug in Redux", or even "a bug with Redux" - the same thing would happen with Context. It just does not happen with Context because you have undefined as initial state in your context, and an object as initial state in your Redux store, which means that your component sees undefined during SSR and in the first few moments of your browser rendering if you use Context, and an object in the Redux case. Since your code seems to rely of the truthness of values, and undefined is falsy while objects are truthy, you see different behaviour in these cases.

I'm really sorry, but I don't believe I can help you further with this - but I really want to suggest that you try to simplify your logic here.

Enkratia commented 4 months ago

@phryneas thank you, i'll take this into consideration.