Reactその他

【React】値の更新によって再レンダリングさせない実装と解説

React
この記事は約3分で読めます。

今回は、Reactにおいて値を更新した時に再レンダリングさせない方法について書きます。

今回の記事の対象者

  • 仕様上、値を更新しない選択は不可能な人
  • 親コンポーネントの状態を更新せざるを得ない人
  • useRefの内部実装のことを深く知らない人

TL;DR

  • useStateは、コンポーネントの状態を更新する
  • useRefは、ライフサイクル中は同じ値を保持する

つまり、useRefを使う必要がある。

そもそも、useRefとは何か?

2019年ごろ、Reactの生みの親であるDan Abramovがこのようなツイートをしていました。

useRef()は基本的にuseState({current: initialValue })[0]です。

Dan Abramov – Twitter

では、Danはどういう意図でこのツイートをしたのか分解していきます。

まず、useStateとは状態を管理するためのReactフックです。
その際、『状態を更新する関数』と『現在の状態の値が含まれる配列』が返されます。

// stateとsetStateをコンソールに出力する
const stateConsole = () => {
    const [state, setState] = useState([]);
    console.log(state, setState);
};

stateConsole();
//=>[], function dispatchSetState(fiber, queue, action) {...}

一方で、useRefはDOMノードや他の値を参照するために使用されます。
そのため、useRef()を使う場合には値を更新してもコンポーネントが再レンダリングされません

このことから、useRef()の場合は以下のように書くことができます。

import { useRef } from 'react';

const refConsole = () => {
  const currentRef = useRef([]); // initialValueに空配列を設定
  console.log(currentRef);
};

refConsole();
//=> {current: Array(0)} 

このコードから分かるように、useRefの中身はつまりuseState ({current: initialValue })[0]ということになります。

useState({ current: initialValue })[0]とは?

先ほど、useRefをコンソールしたときの値に、currentプロパティが含まれていますよね?
このcurrentプロパティは、useRef()最初に呼び出されたときに渡された初期値を意味します。

したがって、useState({current: initialValue})[0]と書いた場合、useStateが返す配列の最初の要素である状態の値(オブジェクト)のみを参照することになります。

つまり、currentプロパティの値であるinitialValueの[0]番目を参照しているということです。

useStateとuseRefのちがい

おさらいに、useStateとuseRefの違いを画像にしてみました。
それぞれに特徴がありますので、適切に使うことでより良い開発ライフを送りたいですね。

結論

useState()とuseRef()は、それぞれ異なる目的で使用されます。

  • useStateは、コンポーネントの状態を更新する
  • useRefは、ライフサイクル中は同じ値を保持する

したがって、どちらを使用するかは、仕様によって判断する必要があります。