Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the selector inside useSelector run twice?

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>
  );
}

enter image description here

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>
like image 798
nikolsqy Avatar asked Jan 21 '21 12:01

nikolsqy


People also ask

Can I use useSelector multiple times?

You may call useSelector() multiple times within a single function component. Each call to useSelector() creates an individual subscription to the Redux Store.

Does useSelector re render?

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.


1 Answers

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.

like image 84
markerikson Avatar answered Oct 17 '22 18:10

markerikson