为什么 useSelector 里面的选择器会运行两次?

nik*_*sqy 5 javascript render reactjs redux react-hooks

为什么 useSelector 里面的选择器会运行两次?

例子

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>
  );
}
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

这是一个工作片段,显示每次 Child 安装时选择器回调运行两次:

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>
  );
}
Run Code Online (Sandbox Code Playgroud)
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
);
Run Code Online (Sandbox Code Playgroud)

mar*_*son 4

因为它既在渲染阶段运行,又在分派操作之后运行。因此,第一个日志在<App>渲染时发生,第二个日志在您单击按钮并调度更新存储状态的操作时发生。

useSelector还在安装组件后再次重新运行选择器,以检查是否存在由于构建组件树时分派操作而导致的任何其他更改。

  • 因为[React在`&lt;StrictMode&gt;`中双重渲染组件](https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/#渲染行为边缘情况)。 (3认同)