BLOG

React/useCallback、useMemo、useRefについて

useCallbackについて

パフォーマンス向上の為のフック

callback関数(イベントハンドラー)をメモ化し、不要な新しい関数インスタンスを作成するのを抑制する。これにより不要な再描画を減らしパフォーマンスが向上する。

メモ化とは、プログラム高速化の為の最適化技法の一つで、関数や関数呼び出しの結果を再利用する為にメモライズ、一時的に保持することを意味します。基本的にフックはuseEffectでもあったようにdependenciesリストと呼ばれる依存配列を引数として受け取り、その値が変わったときに値の更新を行っている。

今回もdependenciesリストが変わったときのみコールバックの再生成が行われる仕組みになる。

useMemoについて

useCallback同様パフォーマンス最適化用フック。使い方も似ている。

useCallbackは関数自体をメモ化するのに対して、useMemoは関数の結果を保持するという違いがある。

useRefについて

もともとReactに備わっていたクラスコンポーネントで使用可能だったcreateRefと機能が同等のもの。useRefは関数コンポーネントで同等の機能を提供するフック。

初回レンダリング時に、インプットフィールドにフォーカスが行くようなアプリを作成する。

・src/components/FocusInput.jsを作成。

・rfceスニペット

・jsx内にインプットフィールドを用意

・初回レンダリング時に処理を行いたいので、useEffectをreactからインポートし、ライフサイクルメソッドを追加する。

・dependenciesリストをemptyarayにすることで、初回レンダリング時のみという条件を加える。

ライフサイクルメソッドの追加、実行準備が整ったのでインプットフィールドに対してアクションを起こす。

・useRefをインポート

・インポートしたuseRefを使って、リファレンスオブジェクトを作成する。

・初期値をnullにする。

・作成したRefオブジェクトをインプットフィールドのRefプロパティにもたせる

・このようにuseRefによって作成された、リファレンスオブジェクトをインプットフィールドのRefプロパティにもたせることにより、Refオブジェクトのカレントプロパティ内にインプットノードが格納され、インプットフィールドにアクセスすることが可能になる。

※補足として、useRefによって作成されたオブジェクトは、すでにカレントプロパティをもっており、useRefによって渡された引数が初期値としてカレントプロパティに格納されている。

・App.jsにインポート

FoucusInput.js

import React, {useEffect, useRef} from 'react'

function FocusInput() {

    const inputRef = useRef(null)
    useEffect(() => {
        inputRef.current.focus()
    },[])
    return (
        <div>
            <input ref={inputRef} type="text" />
        </div>
    )
}

export default FocusInput

App.js

import React, {useReducer, useEffect} from 'react';
import './App.css';
import FocusInput from './components/FocusInput';



function App() {

  return (
    <div className="App">
      <h1>useRef</h1>
      <FocusInput/>
    </div>
  );
}

export default App;

インプットフィールドにフォーカスがあたる画面の出来上がり。

次に別の活用方法を実装。

一秒ごとにカウントアップするアプリを実装

・src/components/Count.jsを作成

・usestate,useEffectをインポート

・countStateをuseStateを使って作成する。※初期値を0とする。

・ライフサイクルメソッドを使いたいので、useEffectを使う。

・setIntervalを使って一秒ごとカウントアップさせる。

・コンポーネントのアンマウント時にクリアインターバルを実行する。

・dependenciesリストにはemptyarrayを渡す。

・jsx内にカウントの表示をする。

・App.jsに読み込む

Count.js

import React,{useState, useEffect} from 'react'



function Count() {
    const [count, setCount] = useState(0)
    useEffect(() => {
        const interval = setInterval(() => {
            setCount(prevCount => prevCount + 1)
        }, 1000);
    },[])
    return (
        <div>
            <h1>{count}秒</h1>
        </div>
    )
}

export default Count

App.js

import React, {useReducer, useEffect} from 'react';
import './App.css';
import Count from './components/Count';



function App() {

  return (
    <div className="App">
      <h1>Count Time</h1>
      <Count/>
    </div>
  );
}

export default App;

次にストップボタンを追加する。

・Count.jsにてストップボタンを追加する。

そうするとエラーが出る。jsx内で参照されてるIntervalが宣言されてないというエラー。これはuseEffect内で宣言した変数が、useEffectのローカル変数として宣言されているので、外部からアクセスできずにこのようなエラーになる。

このエラーをuseRefを使って解決する。

・まずReactからuseRefをインポートする。

・useRefを使ってリファレンスオブジェクトを作成する。

・作成ができたら、intervalファンクションをintervalではなくintervalRefに格納する。

・useRefによって作成されたオブジェクトはcurrentプロパティを持っているので、currentプロパティ内に代入する。(interval.current)

・最後にintervalで読み込んでいるのをintervalRef.currentに置き換える。

Count.js

import React,{useState, useEffect, useRef} from 'react'



function Count() {
    const [count, setCount] = useState(0)
    const intervalRef = useRef()
    useEffect(() => {
         intervalRef.current = setInterval(() => {
            setCount(prevCount => prevCount + 1)
        }, 1000);
        return () => {
            clearInterval(intervalRef.current)
        };
    },[])
    return ( 
        <div>
            <h1>{count}秒</h1>
            <button onClick={() => clearInterval(intervalRef.current)}>ストップ</button>
        </div>
    )
}

export default Count

これで時間を止めれる。