question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

`<Provider>` misses state changes that occur between when its constructor runs and when it mounts

See original GitHub issue

Do you want to request a feature or report a bug?

Bug

What is the current behavior?

If state changes occur during the time after <Provider>'s constructor runs and before <Provider> is mounted, <Provider> will miss those changes and won’t make them available to connected components during the render phase of those components.

This is probably a rare use case, but it can occur in a very specific scenario that’s useful in an app that uses both server-side and client rendering and needs to allow dynamically loaded components (such as components loaded when the route changes) to attach new reducers to an existing store.

Here’s a reduced test case that fails in React Redux 6.0: https://codesandbox.io/s/612k3pv1yz

What is the expected behavior?

Connected components should always see the most recent Redux state, even if that state changed between when <Provider> was constructed and when it was mounted. This was the behavior in 5.x.

Here’s an exact copy of the reduced test case above, but using React Redux 5.1.1 to demonstrate that this works fine there: https://codesandbox.io/s/yvw1vmnkrv

Which versions of Redux, and which browser and OS are affected by this issue? Did this work in previous versions of Redux?

Redux 4.0.1. This worked in React Redux 5.x, but broke in 6.0. It’s unrelated to any specific browser or OS.

More background on this use case

I realize this use case may be a little hard to understand at first glance, so I’ll try to explain in more detail why it’s valuable.

The repro case above simulates a technique that’s useful when using Redux and React Redux in an SSR-compatible app that needs to be able to load components and attach new reducers on demand (such as when the route changes) without knowing up front what components or reducers might eventually be loaded.

This technique is used extensively by Cake. I believe New Twitter uses a similar approach, but they’re still on React Redux 4.x so aren’t yet affected by this bug.

When rendering on the server, we can’t load components during the render phase (because the SSR render phase is synchronous). So instead we need to load all components for a given render pass up front, then attach reducers as needed during the render phase of the app.

Dynamically loaded components may themselves import other components, and components at any level of the import tree could be Redux-connected. This means each component must be able to attach its own reducers. The withRedux decorator in the repro case simplifies this by wrapping a dynamically loaded component (such as the Route component in the example) in a higher order component that attaches reducers on demand in its constructor.

Since React constructs <Provider> before it constructs its children, this means that the Redux store <Provider> sees at construction time doesn’t yet have a complete set of reducers attached.

Once the child components are constructed, all reducers will have been attached and React will begin to render the component tree, but <Provider> in React Redux 6.0 passes the old state to all its context consumers during the render phase, breaking any component that expects the new state. <Provider> doesn’t check for state changes until componentDidMount() runs, at which point it’s already too late.

Edit: s/Redux/React Redux/ in various places because I’m tired. I do know the difference, I promise!

Edit 2: Clarified that dynamically loaded reducers are attached during the render phase, not before it.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:6
  • Comments:59 (43 by maintainers)

github_iconTop GitHub Comments

9reactions
markeriksoncommented, Dec 14, 2018

(side note: I greatly appreciate the detailed issue writeup and the documented CodeSandbox example - thank you for that!)

6reactions
gaearoncommented, Dec 14, 2018

I agree that v6’s behavior totally makes sense given where React is headed.

To be clear from React’s point of view, a library “missing” an update is always a bug. No matter when an update happens, the end result should be consistent.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Can't call setState on a component that is not yet mounted
clickMe.bind(this) from constructor and do it in a lifecycle method ... The setState() is used so that we can reflect the state changes...
Read more >
Accessing the Store | React Redux
#1132: Update docs for using a different store key · #1126: <Provider> misses state changes that occur between when its constructor runs and...
Read more >
Testing state changes in React functional components
Learn how to test your React functional components and the state changes for components that use hooks with Jest and Enzyme as testing ......
Read more >
State and Lifecycle - React
Class components should always call the base constructor with props . Remove the date prop from the <Clock /> element: root.render ...
Read more >
Why hooks are the best thing to happen to React
You can also implement React state and lifecycle methods without writing classes. Below are code examples to illustrate React class and ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Hashnode Post

No results found