React hook useRender 调用两次,如果之后 bailing 和 setting state

React hook useRender called twice if bailing and setting state afterward

提问人:webbyweb 提问时间:5/4/2020 最后编辑:webbyweb 更新时间:5/5/2020 访问量:468

问:

我不确定这是否是预期的行为,但是如果您在使用 useReducer 钩子时退出调度 (https://reactjs.org/docs/hooks-reference.html#bailing-out-of-a-dispatch),则如果操作后跟渲染,则该操作会发生两次。让我解释一下:

// bailing out to prevent re-rendering
const testReducer = (state, action) => {
  switch (action.type) {
    case "ADD":
      state.test += 1
      return state;
  }
};

const myComponent = () => {
  let [totalClicks, setClicks] = useState(0);
    const [state, setState] = useReducer(testReducer, {
      test: 0,
    });

  const clickHandler = () => {
    setState({type: 'ADD'});
    setClicks((totalClicks += 1));
  };

  return (
    <div>
      <button onClick={clickHandler}>+</button>
      <p>{totalClicks}</p>
      <p>test count: {state.test}</p>
    </div>
  );
}

单击该按钮时,state.test 将增加 2,而 totalClicks 将增加 1。但是,如果我要更改减速器,使其不会像下面那样,它们都会增加 1。

// non-bailing reducer
const testReducer = (state, action) => {
  switch (action.type) {
    case "ADD":
      return {
        test: state.test + 1,
      };
  }
};

为什么会这样?这是预期的行为还是错误? 沙盒示例:https://codesandbox.io/s/sad-robinson-dds63?file=/src/App.js


更新:在进行一些调试后,似乎只有在使用 React.StrictMode 包装时才会发生此行为

有谁知道是什么原因造成的???

reactjs 使用状态 use-reducer react-lifecycle-hooks

评论

1赞 chandan_kr_jha 5/4/2020
你能为此创建一个可重现的例子吗?我创建了一个,没有发现任何问题。codesandbox.io/s/hooks-state-non-pure-update-d83uo
0赞 webbyweb 5/5/2020
附加。看了你的之后,我不明白我所做的事情有什么不同,以及为什么我的行为会这样。codesandbox.io/s/sad-robinson-dds63?file=/src/App.js

答:

1赞 hackape 5/5/2020 #1

根据 StrictMode 的文档,react 故意以相同的动作调用 reducer 函数两次,从而暴露未被注意到的潜在有害副作用,这正是您的情况。

严格模式无法自动为您检测副作用,但它可以通过使它们更具确定性来帮助您发现它们。这是通过有意重复调用以下函数来完成的:[...]传递给 useState、useMemo 或 useReducer 的函数

评论

0赞 webbyweb 5/5/2020
哇,这很有趣。那么这个例子是不安全的呢?是不是我们在同一个点击事件上更新了我们的 useState 状态和 useReducer 状态?
0赞 hackape 5/5/2020
这是关于你违反了一个假设,即 reducer 应该是无副作用的、幂等的,并且在发生更改时总是返回新值。你违反了第三条规则(虽然是故意的)。对我来说,这更像是一个惯例。但是,由于围绕这一假设建立了一大堆生态系统和机制(如救助),因此它成为一种要求。所以 strictMode 决定公开它。