首页 > 解决方案 > 将内联 reducer 函数传递给 React.useReducer

问题描述

让我们以内联减速器为例:

const App = () => {
  const [state, dispatch] = React.useReducer(s => {
    console.log("reducer invoked with state", s)
    return s + 1
  }, 0);

  return (
    <div>
      <p>{state}</p>
      <button onClick={dispatch}>Increment</button>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>

在第一次单击时,App会呈现两次,随后的单击只会导致一次重新呈现。

我想了解上述行为useReducer。阅读此答案后,有些事情我仍然不清楚。

  1. 为什么只有第一次触发双重渲染useReducer
  2. 1.) 是否还有其他情况,这也会导致内联减速器的双重渲染?
  3. 什么时候不推荐这种内联减速器模式(性能等)?还是没有缺陷?

标签: reactjs

解决方案


通过将 reducer 函数放在组件中,您可以在每次组件呈现时创建一个新函数。它可能有相同的文本,但 react 无法分辨,所以它被迫运行新旧函数并比较它们的结果。

在下面的代码片段中,我让 reducer 函数注销了正在运行的函数实例,您会看到前两个双重调用是两个不同的函数。我不确定为什么以后不需要也这样做

let count = 0;
const App = () => {
  count++;
  const [state, dispatch] = React.useReducer(s => {
    console.log(`reducer #${count} invoked with state`, s)
    return s + 1
  }, 0);

  return (
    <div>
      <p>{state}</p>
      <button onClick={dispatch}>Increment</button>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>

所以是的,使用内联减速器可以稍微降低性能。因此,如果您不需要内联它,那么您应该将它移到组件之外。

如果您确实需要内联它(例如,因为您需要引用组件内的其他变量),在大多数情况下这可能没什么大不了的。如果是,那么您可以使用Callback 来记忆它,并且只在需要时更改它。

const Example = () => {
  const [size, setSize] = useState(1);
  const reduction = useCallback((s) => {
    console.log(`reducer called; something = ${something}`);
    return s + size;
  }, [something]l
  const [state, dispatch] = useReducer(reduction, 0);

推荐阅读