1
1

More than 1 year has passed since last update.

useCallbackについて学んできたZE★

Posted at

これはなに?

React初学者の私がフロントエンドになるための第一弾!
この記事では、ReactのhooksであるuseCallbackについて学んできました!

とりあえず、useCallbackってなんなん?

useCallbackってなんなんということですが...
 👉結論: パフォーマンス向上のためのhooksです!
ありがとうございました!!
と、いきたいところなのですが、詳細書いていきたいと思います。

useCallbackとは、関数をメモ化することができます。

メモ化とは、いわばキャッシュです。(多分)
Reactでは、親コンポーネントの再描画によって、
変更のない子コンポーネントまで再描画されてしまうことがしばしばあります。

そんな時に使うのが、useCallbackです。

では、関数をメモ化するとはいったいどういうことなのでしょう。

関数をメモ化するとは?

まず、サンプルとして以下のコードを見てみましょう。

Parent.js
const updateCountA1 = () => {
  setCountA((prev) => prev + 1);
};

const updateCountA2 = () => {
  setCountA((prev) => prev + 1);
};

Object.is(updateCountA1, updateCountA2)

上記の場合、Object.is()の返り値は何になるでしょう。
一見、updateCountA1updateCountA2は、
まったく同一の処理を行なっているため、trueが返ってくるように思えます。
が、実際の返り値はfalseとなり、異なるオブジェクトとして判定されます。

これでは、コンポーネントが描画される度に関数が定義されてしまい、パフォーマンスの悪化に繋がります。
そこで活躍するのが、今回のuseCallbackとなるわけです。

では、早速useCallbackの書き方を見ていきましょう。

useCallbackの書き方

ここでは、onClickなどで使う関数を想定してみます。

Parent.js
import { useCallback } from 'react'
~~~
const handleClick = useCallback(() => {
	doSomething(a, b);
},[a, b]);

useCallbackはhooksなのでreactからimportが必要です。

では関数の構成を見ていきましょう。

Parent.js
const handleClick = useCallback(() => {
    //第一引数 コールバック関数
	doSomething(a, b);
  },
//第二引数 依存配列
[a, b]);

useCallbackは以下のような構成になっています。

第一引数 コールバック関数

第一引数には実行したい関数を記述します。
今回の例でいえばonClickで実行する関数にあたります。
doSomething(a, b)の関数が実行されます。

第二引数 依存配列

第二引数には依存配列を記述します。

依存配列が空にする場合

依存配列は[]のように空にすることも可能です。

Parent.js
const Parent = () => {
  const [countA, setCountA] = useState(0);

  const updateCountA = useCallback(() => {
    setCountA(countA + 1);
  }, []);

  return (
    <div>
      <button onClick={updateCountA}>countup A</button>
      <p>A: {countA}></p>
    </div>
  );
};

依存配列を空にした場合は、初回実行時に関数が定義されます。

この場合、updateCountA内のcountAの値は初回実行時の1の値でメモ化されるため、
何度updateCountAの関数を実行しても、countAの値は1のままとなります。

依存配列に値を入れる場合
parent.js
const Parent = () => {
  const [countA, setCountA] = useState(0);

  const updateCountA = useCallback(() => {
    setCountA(countA + 1);
  }, [countA]);

  return (
    <div>
      <button onClick={updateCountA}>countup A</button>
      <p>A: {countA}></p>
    </div>
  );
};

依存配列に格納されている値が変化した場合のみ、関数が再定義されます。

この場合、依存配列にcountAが格納されているため、countAの値が更新されるたびに、
updateCountAの関数は再定義されます。
そのため、countAの値はカウンターの役目を果たすことができます。

実際に使ってみるZE★

useCallbackを使わない場合

Parent.js
import React, { useCallback, useState } from "react";
import Child from "./Child";

const Parent = () => {
  console.log("親コンポーネントrendar");

  const [countA, setCountA] = useState(0);
  const [countB, setCountB] = useState(0);

  const updateCountA = () => {
    setCountA((prev) => prev + 1);
  };

  const updateCountB = () => {
    setCountB(countB + 1);
  };

  return (
    <div>
      <button onClick={updateCountA}>countup A</button>
      <Child onClick={updateCountB} />
      <p>A: {countA}</p>
      <p>B: {countB}</p>
    </div>
  );
};
Child.js
import React, { useCallback, useState } from "react";
import Child from "./Child";

const Parent = () => {
import React from "react";

const Child = React.memo(({ onClick }) => {
  console.log("子コンポーネントrendar");
  return (
    <div className="childComponent">
      <button onClick={onClick}>countup B</button>
    </div>
  );
});

この場合、setCountAが実行された場合でも、
子コンポーネントであるChild.jsが再描画されてしまいます。

useCallbackを使う場合

Parent.js
import React, { useCallback, useState } from "react";
import Child from "./Child";

const Parent = () => {
  console.log("親コンポーネントrendar");

  const [countA, setCountA] = useState(0);
  const [countB, setCountB] = useState(0);

  const updateCountA = () => {
    setCountA((prev) => prev + 1);
  };

  // 先ほどのコードから変更はuseCallbackのみ
  const updateCountB = useCallback(() => {
    setCountB(countB + 1);
  }, [countB]);

  return (
    <div>
      <button onClick={updateCountA}>countup A</button>
      <Child onClick={updateCountB} />
      <p>A: {countA}</p>
      <p>B: {countB}</p>
    </div>
  );
};
Child.js
import React, { useCallback, useState } from "react";
import Child from "./Child";

const Parent = () => {
import React from "react";

const Child = React.memo(({ onClick }) => {
  console.log("子コンポーネントrendar");
  return (
    <div className="childComponent">
      <button onClick={onClick}>countup B</button>
    </div>
  );
});

useCallbackを使うことで、setCountAが実行された場合でも、
子コンポーネントであるChild.jsは再描画されなくなります。

まとめ

useCallbackとは...?のまとめ

  • 関数をメモ化することができる
  • メモ化することで不要なレンダリングを防ぐことができる
  • パフォーマンスを向上させることができる

useCallbackの使い方のまとめ

  • 第一引数にはコールバック関数を記述する
  • 第二引数には依存配列を記載する
    • 依存配列は[]でもOK
      • ただし関数が定義されるのは初回実行時のみ
    • 依存配列に値を格納することができる
      • 格納された値が更新される度に関数が再定義される

では、良きReactライフを!!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1