Description
What is the current behavior?
Demo: https://codesandbox.io/s/race-condition-x1h1d
In this example, our parent component consumes a value from the store, and a child component on render calls an action to update that value multiple times. We see the child component receives the updated value, while the parent component does not after the first update. The parent component's useSelector instance doesn't invoke the selector function and just returns the cached value.
Wrapping the child component's action dispatch in a useEffect solves the issue.
Defining the selector in-line so that each render passes a different function reference, forcing useSelector to call the selector again also works around the issue.
The real-life use case for this pattern is that we have a redirect caused by rendering a react-router redirect component, and this causes an update to the redux store which is storing the current location thru connected-react-router, and the parent component to this consumes location from the store.
import React, { useCallback, useEffect } from "react";
import { incrementAction } from "./actions";
import { useSelector, useDispatch } from "react-redux";
const countersSelector = state => {
console.log("RUNNING SELECTOR", state.counters);
return state.counters;
};
function Child(props) {
const dispatch = useDispatch();
const increment = useCallback(() => dispatch(incrementAction()), [dispatch]);
console.log("Child CALLING USESELECTOR");
const counters = useSelector(countersSelector);
console.log(counters);
// wrapping this in a useEffect gets around the issue
if (counters.counter < 3) {
console.log("CHILD IS INCREMENTING");
increment();
}
return <div>Child: {counters.counter}</div>;
}
function App(props) {
console.log("App CALLING USESELECTOR");
const counters = useSelector(countersSelector);
console.log(counters);
return (
<div className="App">
<div>App: {counters.counter}</div>
<Child />
</div>
);
}
export default App;
What is the expected behavior?
We would expect the parent component's useSelector instance to return the same value from the store as the child component's
Which versions of React, ReactDOM/React Native, Redux, and React Redux are you using? Which browser and OS are affected by this issue? Did this work in previous versions of React Redux?
The versions used in the code sandbox are:
react: 16.13.1
react-dom: 16.8.3
react-redux: 7.1.3
redux: 4.0.5