react-redux
react-redux copied to clipboard
React & redux state mismatch, when using dispatch from useEffect
What version of React, ReactDOM/React Native, Redux, and React Redux are you using?
- React: 18.3.1
- ReactDOM: 18.3.1
- Redux: 4.2.1
- React Redux: 8.1.2
What is the current behavior?
Made a simple 'counter' component that increments a counter when you click a button. It has more indirection than necessary, but it's a simplified version of a bug in some older code I'm looking at. Found it when trying to upgrade react-redux from 7 to 8. (Works fine in 7)
The basic issue is that setPendingIncrement(false)
updates local state, then dispatch(counterActions.setValue(count + 1))
updates Redux state, however on the next render the local state update is not visible (yet) and Redux state change is already visible.
Now, I've read about stale props and zombie children, so I'm aware that the component will eventually re-render with both states in sync. That's fine, since the render function itself has no side-effects. However, I didn't expect the discrepancy to extend to useEffect
. In useEffect, we do have side-effects, e.g. we could send a request to the server.
import {useEffect, useState} from 'react'
import './App.css'
import {useSelector, useDispatch} from 'react-redux';
import {counterActions} from './slices/counterSlice';
function App() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
const [pendingIncrement, setPendingIncrement] = useState(false);
console.log(`RENDER ${count} ${pendingIncrement}`);
const onClick = () => {
setPendingIncrement(true);
}
useEffect(() => {
if (pendingIncrement) {
setPendingIncrement(false);
dispatch(counterActions.setValue(count + 1));
}
}, [pendingIncrement, setPendingIncrement, count, dispatch]);
return (
<button onClick={onClick}>
count is {count}
</button>
)
}
export default App;
Complete example: https://snack.expo.dev/-JCou0gvPMHLWkAO5InqU (snack.expo.dev doesn't let you print to console though, so it's hard to debug there)
What is the expected behavior?
useEffect
is called with React state and Redux state in sync. Or, if this pattern violates some principles of Redux, I'd love to understand the details. I.e. what exactly we can and cannot do.
This code works fine with react-redux 7, so it's at least a regression.
Which browser and OS are affected by this issue?
macos 14.5 / Chrome 126.0.6478.127
Did this work in previous versions of React Redux?
- [X] Yes