LoginSignup
1
1

More than 1 year has passed since last update.

React: useCallback [アウトプット全くしてこなかったのでアウトプットする011]

Last updated at Posted at 2022-03-10

useCallback理由を持って使ったことがなく、前職では「これ使っておけばパフォーマンス上がるんでしょ?使っとこ!」みたいなノリで使っていましたが、ちょっとまずいのでちゃんと理解します(念の為、第二引数の依存配列云々が変化されたときに云々は知って使っていました)。

メモ化されたコールバックを返します。
インラインのコールバックとそれが依存している値の配列を渡してください。useCallback はそのコールバックをメモ化したものを返し、その関数は依存配列の要素のいずれかが変化した場合にのみ変化します。これは、不必要なレンダーを避けるために(例えば shouldComponentUpdate などを使って)参照の同一性を見るよう最適化されたコンポーネントにコールバックを渡す場合に便利です。
出典: https://ja.reactjs.org/docs/hooks-reference.html#usecallback

メモ化とは

useCallbackを使用しない場合、renderが走るたびに関数が生成されるわけだが、そうなるとPureComponent(もしくはReact.memoなコンポーネント)に渡る関数の参照が毎回変わるため、意図としては再レンダリングしないはずの場所で再レンダリングが走ってしまう。

なるほど....

Reactが再レンダリングされるタイミングは以下です。

  • stateが更新された時
  • propsが更新された時
  • 親コンポーネントが再レンダリングされた時

ここでuseCallbackを使用していないとレンダリングされるごとに関数が生成されます。

それを防ぐのがuseCallbackなのですね。。。。

こちらの記事がかなりわかりやすいのでコードを紹介します。
このコードはuseStateでoutputLog関数を依存配列に入れて、この値が変わったらouputLogが実行される
ボタンをクリックしたcountが更新される。つまり再レンダリングされる。

import React, { useEffect, useState } from "react";

export default function App() {
  const [message, setMessage] = useState("");
  const [count, setCount] = useState(0);
  // レンダリングごとに別の関数になる
  const outputLog = (value: string) => {
    console.log(value);
  };
  // 別の関数になるので、レンダリングごとにログが出力されてしまう
  useEffect(() => {
    outputLog(message);
  }, [outputLog]);

  return (
    <>
      <input
        type="text"
        value={message}
        onChange={(e) => setMessage(e.target.value)}
      />
      <button onClick={() => setCount(count + 1)}>click me</button>
    </>
  );
}

codesandbox-react-callback.gif

上の動画を見てもらうとわかるのですが、ボタンを押してレンダリングを走らせるたびにuseEffectで監視しているoutputLogという関数が生成されて、それを検知して処理が走っています。

これをどうにかしてくれるのがuseCallbackです。
outputLogをuseCallbackでメモ化して再度同じ行動をしてみます。

import React, { useEffect, useState } from "react";

export default function App() {
  const [message, setMessage] = useState("");
  const [count, setCount] = useState(0);
  // 関数をメモ化
  const outputLog = useCallback(
    (value: string) => {
      console.log(value);
    },
    [message]
  );
  // 関数をメモ化しているので処理が走らない
  useEffect(() => {
    outputLog(message);
  }, [outputLog]);

  return (
    <>
      <input
        type="text"
        value={message}
        onChange={(e) => setMessage(e.target.value)}
      />
      <button onClick={() => setCount(count + 1)}>click me</button>
    </>
  );
}

こうするとどうなるか

codesandbox-react-callback2.gif

メッセージを変更しない限り、ボタンを押して再レンダリングをしても関数が生成されず、useEffect内の処理が走らないことが確認できました。

これからはちゃんと理解して使っていきます。

参考

強くなりたい!!!!!!!!!!!!!

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