■目次

useCallbackとは

親コンポーネントが再レンダリングされた際に、追従して子コンポーネントも再レンダリングされることを防ぐメソッド。Reactにデフォルトで備わっている機能。

子コンポーネント自身のstateやpropsで受け取ったものに変更があったときだけ再レンダリングされるようになる。

親コンポーネントからpropsで関数を受け取る際に、その関数自身に適用する

<aside> 🌿 アロー関数で作成した関数は、呼び出される度、毎回新しい関数を生成していると判断される そのため戻り値は同じでも、毎回「戻り値が更新された」と子コンポーネントが勘違いして、再レンダリングを起こしてしまう

これは、子コンポーネント自体をmemo化していても発生するので注意!(propsで受け取ったものが更新されたと勘違いするため)

したがって、useCallbackで関数自身をラップしてあげる必要がある

そうすることで、関数の処理が変わらない場合は同じものを使い回すようになり、その結果、本当に戻り値が変わったときだけ再レンダリングされるようになる

</aside>

使い方

▼書き方

/**
* 親コンポーネントに記述している関数(子コンポーネントに渡す関数だけ)にuseCallbackを適用し、
* 第二引数に変更の有無を監視したい要素を指定する
* 
* 第二引数を空にした場合は、(監視すべき要素がないため)毎回同じ処理、結果を返すようになる
* 基本的には、関数内で使用した要素を第二引数にセットしておけば問題ない
*/

**import { useCallback } from "react";**

const HogeHoge = **useCallback(**() => {
	// 省略
}**, [監視対象])**;

▼使用例

/**
* 親コンポーネント
*
* inputは**onClickClose関数**と関係ないため、どれだけテキスト入力しても**ChildArea**が
* 再レンダリングされることはない
* 「表示」ボタンをクリックすると、**ChildArea**に渡しているstateのopenが更新されるため、
* **ChildArea**も再レンダリングされる
*/
import { useState, **useCallback** } from "react";
import { ChildArea } from "./ChildArea";
import "./styles.css";

export default function App() {
  console.log("App");
  const [text, setText] = useState("");
  const [open, setOpen] = useState(false);

  const onChangeText = (e) => {
    setText(e.target.value);
  };

  const onClickOpen = () => {
    setOpen(!open);
  };

  const onClickClose = **useCallback(**() => {
    setOpen(false);
  }**, [setOpen])**;

  return (
    <div className="App">
      <input value={text} onChange={onChangeText} />
      <br />
      <br />
      <button onClick={onClickOpen}>表示</button>
			// ▼子コンポーネントにonClickClose関数を渡す
      <ChildArea open={open} onClickClose={**onClickClose**} />
    </div>
  );
}
/**
* 子コンポーネント
*
* 「閉じる」ボタンをクリックすると、**ChildArea**に渡しているstateのopenが更新されるため、
* **ChildArea**も再レンダリングされる
*/
import { memo } from "react";

const style = {
  width: "100%",
  height: "200px",
  backgroundColor: "khaki"
};

export const ChildArea = memo((props) => {
	// 分割代入
  **const { open, onClickClose } = props;**
  console.log("ChildAreaがレンダリングされた!!");

  const data = [...Array(2000).keys()];
  data.forEach(() => {
    console.log("...");
  });

  return (
    <>
      {open ? (
        <div style={style}>
          <p>子コンポーネント</p>
          <button **onClick={onClickClose}**>閉じる</button>
        </div>
      ) : null}
    </>
  );
});

▼実際の挙動はこちら

Reactに入門した人のためのもっとReactが楽しくなるステップアップコース完全版 3-11. useCallback

参考