当前位置: 首页 > 知识库问答 >
问题:

我们是否应该在React函数组件中的每个函数处理程序中使用useCallback

滑景胜
2023-03-14
const Example = () => {
  const [counter, setCounter] = useState(0);
  
  const increment = () => setCounter(counter => counter + 1); 
  return (
    <div>
      <Button onClick={increment} />
      
      <div>{counter}</div>
    </div>
  );
}

当我将onclick处理程序作为箭头函数传递时,我的eslint抛出一个警告:

error    JSX props should not use arrow functions        react/jsx-no-bind

我从这篇文章中读到的答案是:https://stackoverflow.com/questions/36677733/why-shouldnt-jsx-props-use-arrow-functions-or-bind#:~:text=why%20you%20shownt%20use,previous%20function%20is%20garbam%20collected。

简短的回答是因为arrow函数每次都被重新创建,这会损害性能。本文提出的一个解决方案是用空数组包装在useCallback钩子中。当我改为这个时,eslint警告真的消失了。

const Example = () => {
  const [counter, setCounter] = useState(0);
  
  const increment = useCallback(() => setCounter(counter => counter + 1), []);
  
  return (
    <div>
      <Button onClick={increment} />
      
      <div>{counter}</div>
    </div>
  );
}

这让我真的很困惑?所以对于函数组件,在处理内联函数处理程序时,我应该只写箭头函数(忽略eslint)还是总是将其包装在useCallback中???

共有1个答案

徐卓
2023-03-14

简短的回答是因为arrow函数每次都被重新创建,这会损害性能。

这是一个常见的误解。无论哪种方式,箭头函数每次都会重新创建(尽管对于Usecallback,后续函数可能会立即丢弃)。useCallback所做的是,如果子组件被memoted,则可以不重新呈现使用回调的子组件。

我们先来看看误解。考虑usecallback调用:

const increment = useCallback(() => setCounter(counter => counter + 1), []);

与不使用Usecallback时所拥有的进行比较:

const increment = () => setCounter(counter => counter + 1);

这要简单得多:创建函数。然后它就不必做上面的#2和#3了。

让我们继续讨论usecallback实际上做了什么,这很有用。让我们看看回调的用法:

<Button onClick={increment} />
const { useState, useCallback } = React;

const Button = React.memo(function Button({onClick, children}) {
    console.log("Button called");
    return <button onClick={onClick}>{children}</button>;
});

function ComponentA() {
    console.log("ComponentA called");
    const [count, setCount] = useState(0);
    // Note: Safe to use the closed-over `count` here if `count `updates are
    // triggered by clicks or similar events that definitely render, since
    // the `count` that `increment` closes over won't be stale.
    const increment = () => setCount(count + 1);
    return (
        <div>
            {count}
            <Button onClick={increment}>+</Button>
        </div>
    );
}

function ComponentB() {
    console.log("ComponentB called");
    const [count, setCount] = useState(0);
    // Note: Can't use `count` in `increment`, need the callback form because
    // the `count` the first `increment` closes over *will* be slate after
    // the next render
    const increment = useCallback(
        () => setCount(count => count + 1),
        []
    );
    return (
        <div>
            {count}
            <Button onClick={increment}>+</Button>
        </div>
    );
}

ReactDOM.render(
    <div>
        A:
        <ComponentA />
        B:
        <ComponentB />
    </div>,
    document.getElementById("root")
);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
 类似资料: