今回の記事では、React Hooks(フック)の知っておきたい情報やさまざまなフック、基本的な使い方についてまとめてみたいと思います。
目次
Reactフックって何?
まずはReactフックがどういったものなのか、見ていきましょう。

React Hooksは2019年2月に、Reactのバージョン16.8から追加された機能です。
Reactフックによって、これまでクラスを使わないといけなかったステート管理やライフサイクルメソッドなどが、関数コンポーネントでも実装できるようになりました。
React Hooksのメリット
Reactフックのメリットを挙げてみると、個人的には次のようになるんじゃないかと思います。
- 複雑になりがちなクラスコンポーネントを減らせる
- propsのバケツリレーへの対策
- コンポーネント間でステートを共有しやすくなる
- (ライフサイクルメソッドの分だけ)学習コストが下がる
React Hooksを使うときのルール
Reactフックを利用するためには、次の2つのルールがあります。
- 関数コンポーネントで使う(クラスコンポーネントでは使えない)
- if文やループ、入れ子になった関数の中で定義できない
それでは、まずは基本的なReactフックから見ていきましょう。
基本的なReact Hooks
Reactフックには現在いろいろなフックがありますが、その中でも次の3つがもっとも基本となるフックだと言われています。それぞれ詳しく見ていきましょう????
- useState
- useEffect
- useContext
useState
useStateはクラスコンポーネントのthis.state.○○
やthis.setState({ ○○ })
を使わずにステートを扱えるようになるフックです。
わざわざクラスを作らなくても、関数コンポーネントでステートを扱えるようになります。
useStateの使い方
useStateでは次の2つをセットで使用します。
下のコードで言うと、useStateではcount
というステートの値と、setCount
というcountの値を更新する関数が必要になります。
- ステートの値
- その値を更新するセッター関数
useState()にはステートの初期値を渡す
useState()ではステートの初期値を引数として渡すことが必要になります。
次のコードで言うところのuseState(0)の0がステートの初期値です。
const [count, setCount] = useState(0);
ステートが文字列の場合には空の文字列を渡したり、null
を渡すこともあります。
const [name, setName] = useState('');const [isOnline, setIsOnline] = useState(null);
useStateの初期値に変数を使うとき
useState()に渡す値はもちろん変数でも大丈夫なので、initialStateというコメント的な変数を使って次のように書くこともできます。
const initialState = 0const [count, setCount] = useState(initialState)
ちなみにcountやsetCountのような名前は自由に設定できますが、セッター関数の名前は「set+ステート名」のように使われることが多いです。上の例で言えば、ステートがcountのときのセッター関数はsetCount、nameのときにはsetNameのような感じです。
useStateでカウンターを作ってみる
では、useStateを使って簡単なカウンターを作ってみましょう。
useStateを使うと次のようなコードで、クリックするとカウントアップするカウンターを作ることができます。
import React, { useState } from "react";export default () => {const [count, setCount] = useState(0);return (<div><p>ボタンを{count}回クリックしました</p><button onClick={() => setCount(count + 1)}>クリック!</button></div>);};
countがステートの値、setCountがセッター関数です。
ボタンをクリックするたびにcountの値が+1されていますね。
useEffect
useStateの次はuseEffect
というReactフックについて見ていきましょう。
useEffectは(componentDidMountのような)Reactのライフサイクルメソッドを使わずに、関数コンポーネントのままライフサイクルメソッドと同じようなことができるフックです。
イメージ的には、useEffectは次の3つのライフサイクルメソッドがまとまったものになります。
- componentDidMount
- componentDidUpdate
- componentWillUnmount
useEffectの使いどころ
useEffectはReactアプリケーションが読み込まれたときやコンポーネントのステートの値が変化したときなど、次のようにReactがレンダリングをした後に何か処理を加えたいときに便利です。
- 初期化のタイミング
- タイマー
- チェックボックスのOn/Off
- コンポーネント内のDOMの操作
- HTTPリクエスト
useEffectの基本的な使い方
useEffectの基本的な形は次のようになります。
import { useEffect } from 'react'useEffect(() => {// HTTPリクエストなどのサイドエフェクトreturn () => {// clean up 関数(componentWillUnmount に書くような処理)};}, []);
useEffectを使ってボタンをクリックするたびにtitleタグを更新してみる
useStateに比べると使い方が少し分かりにくいので、まずは実際にuseEffectを使ってみたいと思います。
ここではuseEffectを使って、ボタンをクリックしたタイミングでtitleタグの値を更新する機能を作ってみましょう。
import React, { useState, useEffect } from "react";export default () => {const [count, setCount] = useState(0);useEffect(() => {document.title = `ボタンを${count}回クリックしました`;}, [count]);return (<div><p>ボタンを{count}回クリックしました</p><button onClick={() => setCount(count + 1)}>クリック!</button></div>);};
(分かりにくいかもしれませんが)クリックするたびにDOM(titleタグ)が更新されていますね。
これまでだったらDOMの操作をするときにはcomponentDidMountを使ってDOMが描画された後に処理を加えていましたが、useEffectを使えばクラスベースのcomponentDidMountを使わずに描画を待ってからDOMを操作できるようになります。
最初の読み込み時のみエフェクトを呼び出すには
最初の読み込みのみエフェクトを呼び出したいときは、第2引数(依存配列)の中身を空にしておきます。第2引数に空の配列を渡すことで他のレンダリング時に呼び出されないようになります。初期化するときに便利ですね。
// ○ 初期化のときにだけエフェクトを呼び出し(依存配列あり)useEffect(() => {console.log('読み込み完了')}, []);// × 毎回エフェクトが呼び出されてしまう(依存配列なし)useEffect(() => {console.log('毎回エフェクト呼び出し')});
いつエフェクトを呼び出すか指定するには
上の例では最初の読み込みのみエフェクトを呼び出していましたが、それ以外のタイミングでもエフェクトを呼び出すことができます。
方法は簡単で、第2引数の依存配列の中にステートや関数を入れるだけです。
こうすることで、引数で渡した値が変更されたタイミングでエフェクトを呼び出すことができます。
// ○ countの値が変化したときにエフェクトが呼び出される(依存配列あり)useEffect(() => {document.title = `ボタンを${count}回クリックしました`;}, [count]);// × 毎回エフェクトが呼び出されてしまう(依存配列なし)useEffect(() => {document.title = `ボタンを${count}回クリックしました`;});
上のコードのコメントにも書きましたが、もし第2引数に何も値を渡さないと更新のタイミングで毎回エフェクトが呼び出されてしまうので注意が必要です。
マウントを解除したときの処理を追加するには
useEffectでは、ライフサイクルメソッドのcomponentWillUnmountと同じようなマウント解除の処理を追加できます。
マウント解除の処理を追加するには、次のようにuseEffectの中でreturn
を使います。こうすることで、マウント解除したときの処理を実行することができます。
useEffect(() => {console.log('読み込み完了')return () => {console.log('マウント解除')}}, []);
マウントを解除したときの処理には、setInterval
をReactで使ったときにセットで使うclearInterval
などを書いたりします。
import React, { useState, useEffect } from "react";export default () => {const [timer, setTimer] = useState(0);useEffect(() => {const interval = setInterval(() => {setTimer((timer) => timer + 1);}, 1000);return () => {clearInterval(interval);};}, [timer]);return <p><strong>{timer}</strong> 秒経過しました</p>;};
ただのタイマーなので動画にするほどでもないと思いますけど…上のコードを実行すると次のようになります。
useEffectが使えるようになるまではclearIntervalをcomponentWillUnmount
を使うことが多かったと思いますが、useEffectが出てきたことでcomponentWillUnmountを使わなくても良くなりました。便利ですね。
useLayoutEffectよりuseEffectが推奨されている
ここまでuseEffectについて見てきましたが、useEffectと似たようなフックにuseLayoutEffectというReact公式フックもあります。
ただ、React公式サイトでも推奨しているように、useLayoutEffectではアプリケーションが遅くなる可能性があるので、画面がちらつくなどの問題がなければuseEffectを使う方が良さそうです。useLayoutEffectについてはこのページの下の方で紹介しているので、もしよかったらご確認ください。
useContext
Reactアプリケーションでステートを共有しようとすると、propsをバケツリレーするためにラッパー地獄に落ちてしまうことがありましたが、useContextを使えば地獄に落ちずにステートやデータを共有しやすくなります。
useContextの使いどころ
ひとつの場所でステートの管理をしたいけど、Reduxを使うほど規模の大きいプロジェクトではないときにも使われています。
アプリケーション全体で使いまわしたいテーマなどのスタイル関係や、認証機能の実装などに便利です。
ダークモードとライトモードを切り替える機能を実装してみる
ここではuseContextの使い方として、コンテクストを使ってダークモードとライトモードを切り替える機能を実装してみましょう。
① まずはcreateContext()
useContextフックでコンテクストにアクセスするには、createContext
を使います。
crateContextはコンテクストオブジェクト(ここではthemes)を受け取り、その時点のコンテクスト(ThemeContext)を返してくれます。
import React from "react";import { useState, useContext } from "react";const themes = {light: {color: "#222",backgroundColor: "#eee",},dark: {color: "#ddd",backgroundColor: "#333",},};export const ThemeContext = React.createContext(themes.dark);
② AppコンポーネントをProviderで囲む
アプリケーション全体でthemesコンテクストを利用するために、ThemeContext.Provider
でAppコンポーネントを囲みます。
Material UIを使うときに<MuiThemeProvider theme={theme}>
でAppコンポーネントで囲むのと同じような感じです。
value
にはオブジェクト形式でダークモード用の配色(themes.dark)を渡します。
import { ThemeContext } from './ThemeContext';const App = () => {return (<ThemeContext.Provider value={themes.dark}>// ...</ThemeContext.Provider>);};
③ コンテクストを利用する
コンポーネントの中で作成したコンテクストを使うには、useContext
やThemeContext.Consumer
を使います。
ThemeContext.ConsumerでもuseContextでも、どちらの方法を使っても大丈夫なのですが、ThemeContext.Consumerだとreturnブロックの中でしか使えないのと、個人的にはuseContext
の方が使いやすいと思うので、ここではuseContextでデモ用のコンポーネントを作ってみます。
import { useContext } from "react";import { ThemeContext } from './ThemeContext';const LoremIpsum = () => {const theme = useContext(ThemeContext);return (<div style={{ backgroundColor: theme.backgroundColor }}><p style={{ color: theme.color }}>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Explicabo,quis!</p></div>);};
ちなみに先ほどのコードでvalue={themes.dark}
と指定したので、theme
オブジェクトにはダークモード用の配色がオブジェクト形式で入っている状態です。
④ ここまでのコードをまとめると
ここまでのコードをまとめると、下のようになります。
これでダークモードとライトモードを切り替える機能の完成です。
import React, { useState, useContext } from "react";const themes = {light: {color: "#222",backgroundColor: "#eee",},dark: {color: "#ddd",backgroundColor: "#333",},};export const ThemeContext = React.createContext(themes.dark);const LoremIpsum = () => {const theme = useContext(ThemeContext);return (<p style={{ color: theme.color, backgroundColor: theme.backgroundColor, padding: '1rem' }}>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Explicabo, quis!</p>);};const App = () => {const [isDark, setDark] = useState(true);const currentTheme = isDark ? "dark" : "light";return (<ThemeContext.Provider value={themes[currentTheme]}><LoremIpsum /><button onClick={() => setDark(!isDark)}>テーマカラーを変更</button></ThemeContext.Provider>);};export default App;
その他のReact公式フック
ここまで3つの基本的なReactフックを紹介してきましたが、Reactには上で紹介したフック以外にも次のような公式フックがあります。
- useRef
- useReducer
- useMemo
- useCallback
- useLayoutEffect
- useDebugValue
それぞれどのようなフックなのか見ていきましょう。
useRef
useRefはReact要素やrenderメソッドで生成されたDOMノードにアクセスできるフックです。
要素にフォーカスするときなど、実際のDOMへのアクセスが必要なときに役に立ちます。
const textInput = useRef(initialValue);
useRefの使用例
下のコードでは、ボタンをクリックしたときに入力フォームにフォーカスします。
import React, { useRef } from "react";export default () => {const textInput = useRef(null);const focusInput = () => textInput.current.focus()return (<div className="App"><input type="text" ref={textInput} /><button onClick={focusInput}>Inputの内容を編集する</button></div>);};
createRefでもuseRefと同じような機能を実装できる
上ではuseRefというReactフックを使って書きましたが、createRef
でも同じような機能を実装することができます。上のコードをcreateRefで書き直すと次のようになります。
import React, { createRef } from "react";export default () => {const textInput = createRef();const focusInput = () => textInput.current.focus();return (<div className="App"><input type="text" ref={textInput} /><button onClick={focusInput}>Inputの内容を編集する</button></div>);}
useRefとcreateRefの違いは?
createRef
ではすべてのレンダリングで新しい参照を返しますが、useRef
では毎回同じ参照を返します。そのため、useRefではコンポーネントが再びレンダリングされても.current
に同じ値を持たせることができます。
※ createRefはuseRefより前のバージョン16.3から追加されました。
useMemo
useMemoは指定した関数をレンダリングごとに呼び出さないようにしてくれるフックです。
useMemoは最初のレンダリングで一度だけ計算を実行し、他のすべてのレンダリングではキャッシュされた値を返してくれるので、通常は計算量が膨大になる関数の最適化のために使われます。
ただ、気軽に使って逆にパフォーマンスを落としてしまう可能性もあるので、必要に迫られたら使うくらいの感覚で良いかもしれません。
雰囲気で使わない React hooks の useCallback/useMemo
useMemoの使い方
useMemoはuseEffectやuseCallbackと同じように、アロー関数の中で使いたい関数を呼び出します。もしuseMemoに渡す関数が引数を受け取る場合には、配列(依存配列)の中に使いたい引数を含めれば大丈夫です。
import React, { useMemo } from 'react';const example = useMemo(() => expensiveCalculation(a), [a]);
useEffectと同じようにuseMemoの依存配列(引数)は空にしても大丈夫ですし、複数入れてもOKです。
const example = useMemo(() => expensiveCalculation(a, b), [a, b]);
また、React.memoを使うとライフサイクルメソッドのshouldComponentUpdateを実現できるようです。
useCallback
useCallback
は依存配列を渡すことで、必要のないレンダリングを防いでくれるReact公式フックです。
useCallbackの使い方
useCallbackはuseEffect
やuseMemo
と同じように、インラインでコールバック関数を渡します。
import React, { useCallback } from 'react'const example = useCallback(() => expensiveCalculation(a, b), [a, b]);
引数がなくてもuseCallbackには依存配列は必要
useCallbackはuseEffect
同じように、依存配列を渡さないと呼び出しのたびに新しい値が返ってきてしまうので、コールバックで引数を使わなかったとしても空の配列を渡してあげます。
// ○(依存配列あり)const example = useCallback(() => expensiveCalculation(), []);// ×(依存配列なし:毎回新しい値が返ってきてしまう)const example = useCallback(() => expensiveCalculation());
useMemoとuseCallbackの違い
useCallbackと似ているフックにuseMemo
がありますが、useCallbackとuseMemoの違いは次のようになります。
- useMemoは値を返し、useCallbackはコールバック関数を返す
- useCallbackは依存配列の要素が変更されるとレンダリング
useMemoは値を、useCallbackはコールバック関数を返す
useMemoとuseCallbackは目的や使い方など似ているところもありますが、useCallbackは値ではなくメモ化されたコールバック関数が返されます。
そのため、もし膨大な計算処理が必要なときには、useCallbackよりも計算結果(値)をキャッシュしてくれるuseMemoを使ったほうが効率が良さそうです。
useCallbackは依存配列の要素が変更されるとレンダリング
useCallbackもuseMemoも依存配列に要素を渡しますが、useCallback
はuseMemoと違って、依存配列の要素が変更されたタイミングで再レンダリングされてコールバック関数を返します。
useLayoutEffect
useLayoutEffect
はほとんどの場合でuseEffectと同じようなReact公式フックです。
useEffectやuseLayoutEffectは次のようなライフサイクルメソッドに対応しています。
- componentDidMount
- componentDidUpdate
- componentWillUnmount
useLayoutEffectとuseEffectはシグネチャーが同じなので、引数や返り値、例外、利用可能性の情報(publicやstaticなど)といった入力と出力が同じになります。
useEffectとuseLayoutEffectの違い
useEffectとuseLayoutEffectの違いとしては処理が実行されるタイミングになります。
useEffectの場合
useEffectの場合には、useEffectの実行を待たずに画面がレンダリングされます。
- コンポーネントをレンダリング
- 画面を更新
- useEffectを実行
useLayoutEffectの場合
useLayoutEffectの場合には、コールバック関数が完了するのを待ってからレンダリングされます。
useLayoutEffect
はすべてのDOMの変更後に、同期的にコールバック関数が実行されます。
- コンポーネントをレンダリング
- useLayoutEffectを実行
- 画面を更新(ReactはuseLayoutEffectが終了するのを待ってから画面を更新)
useLayoutEffectの基本的な使い方
useLayoutEffectの基本的な使い方は次のようになります。
import React, { useLayoutEffect } from 'react'useLayoutEffect(() => {// HTTPリクエストなどのサイドエフェクトreturn () => {// clean up 関数(componentWillUnmount に書くような処理)};}, []);
画面のちらつきが気になる場合にはuseLayoutEffectを
上のようにuseLayoutEffectとuseEffectは処理のタイミングが違うので、もしuseEffectで画面のちらつきが起きてしまうような場合にuseLayoutEffect
を試してみると収まるかもしれません。
useLayoutEffectの場合にはコールバック関数が完了するのを待ってからレンダリングされるので、処理の順番的にuseEffectの方が早めの段階で実行されます。公式サイトでも推奨しているように、まずはuseEffectを利用してみて、それでも問題が残る場合にはuseLayoutEffectを使うくらいの感じが良さそうですね。
useDebugValue
useDebugValueはカスタムフックをReact Developer Tools(Chrome拡張機能)に表示させることができます。
useDebugValueの基本的な使い方
useDebugValueはカスタムフックの中で表示したい値をuseDebugValueに渡します。
import React, { useDebugValue } from 'react';useDebugValue(value);
さいごに
今回はReact Hooksについてまとめてみました。
フックによっては普段なかなか使わないものもあると思いますが、useState
やuseEffect
などのフックは個人的にはかなり便利だと思います。もしまだフックをそこまで使っていないという方は、ぜひこれを機会に試してみてください。
それでは今回はこのあたりで。閲覧ありがとうございました。