useRef / React Hooks - React入門

useRef / React Hooks – React入門

連載目次 : React入門
前回の記事 : useMemoとuseCallback / React Hooks
次回の記事 : useReducer/ React Hooks

今回はReact Hooksfunctionコンポーネントの再描画を抑制することができるuseRefについて解説をします。

要素の参照

classコンポーネント利用時にはref属性を利用して要素を参照することができましたが、functionコンポーネントではuseRefを利用して要素の参照を行います。

次のサンプルではuseRefでinput要素への参照オブジェクトを作成してref属性でinput要素と関連付けを行っています。button要素がクリックされた際にinputEl.currentでinput要素のアクセスすることができます。

function App() {
  
  const inputEl = useRef(null)
  const [text,changeText] = useState("")
  const handleClick = useCallback(()=>{
    changeText(inputEl.current.value)
  },[])

  return (
    <>
      <p>text : {text}</p>
      <input ref={inputEl} type="text" />
      <button onClick={handleClick}>set text</button>
    </>
  )
}

同様のコードをuseRefを利用せずにuseStateを利用して記述した場合は以下のようになります。

function App() {
  const [tmpText,changeTmpText] = useState("")  
  const [text,changeText] = useState("")

  const handleClick = useCallback(()=>{
    changeText(tmpText)
  },[tmpText])

  return (
    <>
      <p>text : {text}</p>
      <input
        value={tmpText}
        onChange={e => changeTmpText(e.target.value)}
        type="text"
      />
      <button onClick={handleClick}>set text</button>
    </>
  )
}

入力中の文字列をステート「tmpText」に格納しておきボタンが押されたタイミングでステート「text」に代入することで同様の挙動を実装することができます。

useRefを利用するかどうかの大きな違いはコンポーネントの再描画が発生するかどうかです。

useRefを利用していないパターンではステート「tmpText」とステート「text」の更新時にコンポーネントの再描画が発生しますが、useRefを利用しているパターンではステート「text」の更新時にのみコンポーネントの再描画が発生します。

値の参照

Classコンポーネント時のref属性の利用方法や先程解説した要素へのアクセス方法からuseRefはDOMへのアクセスを保存するもののように思われがちですが、Classのインスタンス変数と同様な利用方法が可能です。

次のサンプルではuseRefを利用して変数「count」を作成してadd coutnボタンが押された際に1づつ加算していき、show logボタンが押されたタイミングでConsole上に現在の現在のカウントを表示することができるスクリプトです。

function App() {
  const count = useRef(0);

  const addCount = useCallback(()=>{
    count.current += 1
  },[])

  const showLog = useCallback(()=>{
    console.log(count.current)
  },[])

  return (
    <>
      <button onClick={addCount}>add count</button>
      <button onClick={showLog}>show log</button>
    </>
  )
}

変数の初期値はuseRefの引数に指定ができ今回の初期値は0にしています。変数はcurrentプロパティに格納されているのでcount.currentに対して変更を行うことができ、呼び出し時も直接count.currentを呼び出します。

これをuseStateを利用して記述する場合は以下のようになります。

function App() {
  const [count, changeCount] = useState(0);

  const addCount = useCallback(()=>{
    changeCount(prevCount => prevCount+1)
  },[])

  const showLog = useCallback(()=>{
    console.log(count)
  },[count])

  return (
    <>
      <button onClick={addCount}>add count</button>
      <button onClick={showLog}>show log</button>
    </>
  )
}

こちらも大きな違いはコンポーネントの再描画が発生するかどうかです。useStateを利用している場合はステートcountの値に変更が発生する度にコンポーネントの再描画が発生しますがuseRefを利用している場合はコンポーネントの再描画が発生しません。

コンポーネントの再描画や子コンポーネントの再描画は行いたくないが内部の値のみ更新したい場合などでは値をuseStateで定義するのではなくuseRefを利用するのがよいでしょう。

次回は複数のステートをまとめて制御できるuseReducerについて解説をおこないます。

連載目次 : React入門
前回の記事 : useMemoとuseCallback / React Hooks
次回の記事 : useReducer/ React Hooks

【告知】弊社のメンバーが執筆した書籍が発売されました!

弊社のメンバーが執筆した『初心者からちゃんとしたプロになる JavaScript基礎入門』が発売されました! JavaScriptの基礎からVue.jsまでをカバーしており、初学者から基本の復習をしたい方におすすめの1冊です。