useCallback / useMemo在React中做了什么?

问题描述 投票:34回答:2

docs中所述,useCallback返回一个memoized回调。

传递内联回调和一组输入。 useCallback将返回一个回忆的memoized版本,该版本仅在其中一个输入发生更改时才会更改。将回调传递给依赖于引用相等性的优化子组件以防止不必要的渲染(例如,shouldComponentUpdate)时,这非常有用。

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

但是它如何工作以及在React中最好使用它?

附:我认为使用codepen example进行可视化将有助于每个人更好地理解它。 Explained in docs

javascript reactjs react-hooks
2个回答
67
投票

当您希望防止不必要的重新渲染以获得更好的性能时,最好使用此方法。

比较这两种将回调传递给从React Docs获取的子组件的方法:

1. Arrow Function in Render

class Foo extends Component {
  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <Button onClick={() => this.handleClick()}>Click Me</Button>;
  }
}

2. Bind in Constructor (ES2015)

class Foo extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <Button onClick={this.handleClick}>Click Me</Button>;
  }
}

假设<Button>被实现为PureComponent,第一种方式将导致<Button>每次<Foo>重新渲染时重新渲染,因为在每个render()调用中创建了一个新函数。在第二种方式中,handleClick方法仅在<Foo>的构造函数中创建一次,并在渲染之间重用。

如果我们使用钩子将两种方法转换为功能组件,那么它们就是等价物(有点):

1. Arrow Function in Render -> Un-memoized callback

function Foo() {
  const handleClick = () => {
    console.log('Click happened');
  }
  return <Button onClick={handleClick}>Click Me</Button>;
}

2. Bind in Constructor (ES2015) -> Memoized callbacks

function Foo() {
  const memoizedHandleClick = useCallback(
    () => console.log('Click happened'), [],
  ); // Tells React to memoize regardless of arguments.
  return <Button onClick={memoizedHandleClick}>Click Me</Button>;
}

第一种方法是在每次调用函数组件时创建回调,但在第二种方式中,React会为您记忆回调函数,并且不会多次创建回调。

在大多数情况下,第一种方式做得很好。正如React docs所述:

在渲染方法中使用箭头函数是否可以?一般来说,是的,没关系,并且它通常是将参数传递给回调函数的最简单方法。

如果您确实遇到性能问题,请务必优化!


6
投票

我做了一个小例子来帮助其他人更好地理解它的行为方式。您可以运行演示here或阅读下面的代码:

import React, { useState, useCallback, useMemo } from 'react';
import { render } from 'react-dom';

const App = () => {
    const [state, changeState] = useState({});
    const memoizedValue = useMemo(() => Math.random(), []);
    const memoizedCallback = useCallback(() => console.log(memoizedValue), []);
    const unMemoizedCallback = () => console.log(memoizedValue);
    const {prevMemoizedCallback, prevUnMemoizedCallback} = state;
    return (
      <>
        <p>Memoized value: {memoizedValue}</p>
        <p>New update {Math.random()}</p>
        <p>is prevMemoizedCallback === to memoizedCallback: { String(prevMemoizedCallback === memoizedCallback)}</p>
        <p>is prevUnMemoizedCallback === to unMemoizedCallback: { String(prevUnMemoizedCallback === unMemoizedCallback) }</p>
        <p><button onClick={memoizedCallback}>memoizedCallback</button></p>
        <p><button onClick={unMemoizedCallback}>unMemoizedCallback</button></p>
        <p><button onClick={() => changeState({ prevMemoizedCallback: memoizedCallback, prevUnMemoizedCallback: unMemoizedCallback })}>update State</button></p>
      </>
    );
};

render(<App />, document.getElementById('root'));
© www.soinside.com 2019 - 2024. All rights reserved.