web-dev-qa-db-ja.com

ReactでuseCallback / useMemoは何をしますか?

docs で述べたように、useCallbackはメモされたコールバックを返します。

インラインコールバックと入力の配列を渡します。 useCallbackは、入力の1つが変更された場合にのみ変更されるコールバックのメモバージョンを返します。これは、不要なレンダリングを防止するために参照の等価性に依存する最適化された子コンポーネントにコールバックを渡すときに役立ちます(shouldComponentUpdateなど)。

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

しかし、それはどのように機能し、Reactではどこで使用するのが最適ですか?

追伸 codepen example を使用した視覚化は、誰もがそれをよりよく理解するのに役立つと思います。 ドキュメントで説明

39
RTW

これは、パフォーマンスを向上させるために不要な再レンダリングを防止する場合に最適です。

React Docs から取得した子コンポーネントにコールバックを渡すこれら2つの方法を比較します。

1.レンダリングの矢印機能

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

2.コンストラクターでバインド(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()呼び出しごとに新しい関数が作成されます。 2番目の方法では、handleClickメソッドは<Foo>のコンストラクターで一度だけ作成され、レンダリング全体で再利用されます。

フックを使用して機能コンポーネントへの両方のアプローチを翻訳する場合、これらは同等のもの(並べ替え)です:

1.レンダリングの矢印機能->メモ化されていないコールバック

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

2.コンストラクターでバインド(ES2015)->メモ化されたコールバック

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

最初の方法は、機能コンポーネントのすべての呼び出しでコールバックを作成しますが、2番目の方法では、Reactがコールバック関数をメモし、コールバックは複数回作成されません。

ほとんどの場合、最初の方法で問題ありません。 Reactドキュメントの状態として:

レンダリングメソッドで矢印関数を使用しても大丈夫ですか?一般的に言って、はい、それは大丈夫で、多くの場合、コールバック関数にパラメーターを渡す最も簡単な方法です。

パフォーマンスの問題がある場合は、必ず最適化してください!

80
Yangshun Tay

私は他の人がそれがどのように動作するかをよりよく理解するのを助けるために小さな例を作りました。デモを実行することができます 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'));
6
Barbu Barbu