Why does the selector inside useSelector run twice?
Example
const selector = (state) => {
console.log("invoke Selector");
return state;
};
function App() {
console.log("render App");
const count = useSelector(selector);
const dispatch = useDispatch();
return (
<div className="App">
<button onClick={() => dispatch({ type: "INCREMENT" })}>Increment</button>
<button onClick={() => dispatch({ type: "DECREMENT" })}>Decrement</button>
<p>{count}</p>
</div>
);
}
Here is a working snippet showing the selector callback is run twice every time Child mounts:
const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
function count(state, action) {
console.log('reducing action:',action.type)
switch (action.type) {
case "INCREMENT":
return {...state,count:state.count+1};
case "DECREMENT":
return {...state,count:state.count-1};
default:
return state;
}
}
const store = createStore(count,{count:0});
const selector = (state) => {
console.log("invoke Selector",state);
return state.count;
};
function Child() {
console.log("render Child");
const count = useSelector(selector);
const dispatch = useDispatch();
return (
<div className="App">
<button onClick={() => dispatch({ type: "INCREMENT" })}>Increment</button>
<button onClick={() => dispatch({ type: "DECREMENT" })}>Decrement</button>
<p>{count}</p>
</div>
);
}
const App = () => {
const [show,setShow] = React.useState(true);
return (<div>
<button onClick={()=>setShow(s=>!s)}>toggle child</button>
{show?<Child />:'none'}
</div>)
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<div id="root"></div>
You may call useSelector() multiple times within a single function component. Each call to useSelector() creates an individual subscription to the Redux Store.
With useSelector() , returning a new object every time will always force a re-render by default. If you want to retrieve multiple values from the store, you can: Call useSelector() multiple times, with each call returning a single field value.
Because it runs both in the render phase, and after an action is dispatched. So, the first log happens when <App>
is rendered, and the second log happens when you click the button and dispatch an action that updates the store state.
useSelector
also re-runs the selector a second time after the component has mounted, to check if there are any other changes due to actions being dispatched while the component tree was being constructed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With