matsutoba’s blog

フロントエンドエンジニアをしています

Reactのメモ化の覚書

いつも忘れるReactのメモ化関連のメモ。

定数値はコンポーネントの外に出す

コンポーネント内のオブジェクトは毎回インスタンス生成されてしまうので、コンポーネントの外に出す。

// 普通に書いたとき
const ButtonElement = () => {
  const foo = ['a', 'b'];
}
// 定数はコンポーネントの外に出す
const foo = ['a', 'b'];
const ButtonElement = React.memo(() => {
})

propsが変わらない限り描画内容が変化しないコンポーネントはメモ化する

propsに依存しない固定表示のコンポーネントの場合、親の再描画に伴って再レンダリングされる必要がないので、React.memoでメモ化して再レンダリングを防ぐ。

// 普通に書いたとき
const ButtonElement = (props) => {
  return <button>{props.name}</button>
}
// メモ化
const ButtonElement = React.memo((props) => {
  return <button>{props.name}</button>
})

コンポーネントをメモ化していないとき、propsに依存するオブジェクトはuseMemoでメモ化する

コンポーネントをメモ化していないとき、親コンポーネントが再レンダリングに伴いレンダリングが発生する。このとき、コンポーネント内にオブジェクトがあると、毎回インスタンスを生成してしまう。

※ ここで、オブジェクトとは const words = ['a', 'b'] のような配列やJSONオブジェクトのこと。const words = 'a' であれば問題ない。

propsに依存するようなオブジェクトの場合、コンポーネントの外側に出すことができない。この場合は useMemo を使ってメモ化する。

// 普通に書いたとき
const ButtonElement = (props) => {
  const words = props.label.split(" ")
  useEffect(() => {
    console.log("Button rendered", new Date());
  });

  useEffect(() => {
    console.log("Words changed", new Date()); //毎回実行される
  },[words])

  return <button>{props.name} : {words.length}</button>
}
// 普通に書いたとき
const ButtonElement = (props) => {
  const words = useMemo(() => props.label.split(" "), [props.label]);
  useEffect(() => {
    console.log("Button rendered", new Date());
  });

  useEffect(() => {
    console.log("Words changed", new Date()); // 初回だけ実行される
  },[words])

  return <button>{props.name} : {words.length}</button>
}

コンポーネントのpropsに関数が含まれる場合 - useCallbackを使うパターン

コンポーネントの描画のたびに新しい関数インスタンスが作られてしまい、再レンダリングしてしまうので、渡す関数をuseCallbackでラップしておくことで関数インスタンスが作られないようにする。

// ふつうに書いたとき
const fn = () => { console.log("OK") };

const Foo:React.FC<{ param:string, fn: ()=>void }> = React.memo(({param, fn) => {
  return <button>OK</button>
})
// useCallbackを使う
const fn = useCallback(() => { console.log("OK") },[]);

const Foo:React.FC<{ param:string, fn: ()=>void }> = React.memo(({param, fn) => {
  return <button>OK</button>
})

コンポーネントのpropsに関数が含まれる場合 - React.memoの第2引数を使うパターン

渡す関数はそのままで、関数を渡される側でReact.memoの第2引数に再レンダリングの判定条件を設定しておく。

const fn = () => { console.log("OK") };

const Foo:React.FC<{ param:string, fn: ()=>void }> = React.memo(({param, fn) => {
  return <button>OK</button>
}, (prevProps, nextProps) => prevProps.param === nextProps.param );
// 第2引数がtrueだと初回だけ描画。falseだと毎回描画