概要
Reactのフックを初心者でも雰囲気をつかめるように超ざっくりまとめる😊
フックって何?
✅関数コンポーネントをパワーアップさせる関数use〇〇
を総称してフックという。
とりあえず抑えておきたいポイント
- React16.8で登場して関数コンポーネントを強化してくれた。
- クラスコンポーネントでは使えない。
-
多数のフック関数
use〇〇
があり、関数コンポーネントパワーアップさせてくれる。(後述)
フックのいいところ
(フック登場前)
複雑な機能(状態管理など)が関数コンポーネントでは使えなかった🥲
→代わりにクラスコンポーネントで書いていた。
(フック登場後)
複雑な機能(状態管理など)が関数コンポーネントで使えるようになった😊
→関数コンポーネントでほとんど書けるようになった。
結論
- フックのおかげで関数コンポーネントでできることが増えた!
- フック登場後はあまりクラスコンポーネントを使わなくなった!
フックの一覧早見表
代表的なフックの一覧
フック | 役割 | 使用例 | 重要度 |
---|---|---|---|
useState | コンポーネントの状態を管理するためのフック | UIのトグル状態(開閉など)の管理 | 高 |
useEffect | コンポーネントに追加の処理(データ取得、イベントリスナー設定など)を加えるフック | 特定のタイミングでAPIからデータを取得 | 高 |
useContext | アプリ全体で共有されているデータに簡単にアクセスできるようにするフック | テーマや言語設定の設定を取得 | 中 |
useReducer | 複雑なデータ管理を行うフック | フォーム入力の複合的なバリデーション | 中 |
useCallback | メモ化されたコールバック関数を生成するフック | イベントハンドラの再生性を防ぐ | 中 |
useMemo | メモ化された値を生成するフック | 不要な計算処理の実行を防ぐ | 中 |
useRef |
特定のDOM要素などの参照を保持するフック
(getElementByIdのReact版) | 特定のDOM要素のサイズを計測する。 | 中 |
カスタムフック | 自分で定義するフック | データ取得処理を関数化して複数コンポーネントで使い回す。 | 中 |
useImperativeHandle | 子コンポーネントから親コンポーネントへ特定の機能や値を提供するフック | 親コンポーネントから子コンポーネントのアニメーションを開始・停止する。 | 低 |
useLayoutEffect | 画面を再描画する直前に、副作用(DOM要素のサイズ計算など)を行うフック。 | 再描画時のちらつきを防ぐ。 | 低 |
useDebugValue | カスタムフックのデバッグ用フック | 低 |
フックを1つずつ解説
✅それぞれのフックが何をするものか簡潔にまとめる。
useState(重要度:高)
特徴
コンポーネントの状態を管理するためのフック。
使用シチュエーション
- 入力フォームの入力状態(テキスト入力など)を管理
- UIのトグル状態(開閉など)の管理
サンプルコード
const [count, setCount] = useState(0);
具体例
ユーザーのクリック回数をカウントし、表示する。
import React, { useState } from 'react';
function ClickCounter() {
// 状態変数「count」と、その状態を更新するための関数「setCount」を定義する。
// 引数は「count」の初期値になる。0を指定する。
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
{ /*ボタンがクリックされたとき、countの値を1増やす。*/ }
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
export default ClickCounter;
useEffect(重要度:高)
特徴
コンポーネントに追加の処理(データ取得、イベントリスナー設定など)を加えるフック。
初回レンダリング時、再レンダリング時などに処理を実行させることができる。
使用シチュエーション
- 初回レンダリング時にAPIからデータを取得
- 再レンダリング時にウィンドウサイズの変更を監視
サンプルコード
useEffect(() => { fetchData().then(data => setData(data)); }, []);
具体例
初回レンダリング時に、コンソールにメッセージを表示する。
import React, { useEffect } from 'react';
function ComponentWithEffect() {
// 第一引数には、実行したい関数を定義する。
// 第二引数には、依存する値のリストを定義し、いつ関数が実行されるか定義する。
// ここでは空の配列を渡しているので(依存なし)、初回レンダリング時に一度だけ実行される。
useEffect(() => {
console.log('Component has mounted!');
}, []);
return <div>Hello, World!</div>;
}
export default ComponentWithEffect;
useContext(重要度:中)
特徴
アプリ全体で共有されているデータ(テーマの設定やユーザー情報など)を、個々のコンポーネントで簡単に利用するためのフック。
使用シチュエーション
- テーマや言語設定
- ユーザー認証の状態
サンプルコード
const theme = useContext(ThemeContext);
具体例
テーマの設定をアプリ全体で共有し、表示する。
import React, { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
function ThemeViewer() {
// ThemeContextから現在のテーマ(ここでは色)を取得する。
const theme = useContext(ThemeContext);
return (
// 取得したテーマの色をスタイルとして適用する。
<div style={{ color: theme.color }}>
Current theme color is {theme.color}
</div>
);
}
export default ThemeViewer;
【補足】事前にReact.createContext
でコンテキストを作成しておく必要がある
useContext
は事前に定義されたコンテキストを使うもの。
そのため事前にReact.createContext
でコンテキストを作成しておく必要がある。
例:事前にテーマの色を定義しておく
import React from 'react';
// テーマの色を定義
const defaultTheme = {
color: 'blue'
};
// React.createContextを使用してコンテキストを作成する
export const ThemeContext = React.createContext(defaultTheme);
useReducer(重要度:中)
特徴
複数の状態を組み合わせたり、一連のアクションに応じて状態を更新するなど、複雑なデータ管理を行うフック。
使用シチュエーション
- ショッピングカートやゲームの状態管理
- フォーム入力の複合的なバリデーション
サンプルコード
const [cart, dispatch] = useReducer(cartReducer, initialCart);
具体例
カウンターの値を増減させる。
import React, { useReducer } from 'react';
// useReducerで使う。
// この関数は「現在の状態」と「アクション」を引数に取り、「新しい状態」を返す。
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
};
const Counter = () => {
// useReducerフックを使用して、「reducer関数」と「初期状態」を渡す。
// これにより、「状態」と「その状態を更新するためのdispatch関数」が返される。
const [state, dispatch] = useReducer(reducer, { count: 0 });
// dispatch({ type: 'increment' })を呼び出すと、reducer関数が呼び出され、
// 「現在の状態(例:1)」と「アクション(例:increment)」が引数として渡される。
// reducer関数は、「新しい状態(例:2)」を返す。
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+1</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
</div>
);
};
export default Counter;
useCallback(重要度:中)
特徴
メモ化されたコールバック関数を生成するフック。
「レンダリングのたびに中身が変わらない関数」をメモ化(再生成防止)してパフォーマンスを向上させる。
使用シチュエーション
-
クリックされたとき「こんにちは!」と表示するだけのボタンがある。
この「こんにちは!」と表示する関数をメモ化する。
(関数は常に「こんにちは!」を表示するだけなので再生成不要→パフォーマンスが向上する)
サンプルコード
const handleClick = useCallback(() => console.log('こんにちは!');, []);
具体例
ボタンクリック時のイベントハンドラをメモ化する。
(コンポーネントが再レンダリングされてもイベントハンドラを再生成しなくなる)
import React, { useCallback } from 'react';
function SimpleButton() {
// 初回レンダリング時のみ関数が生成される
const handleClick = useCallback(() => {
console.log('こんにちは!');
}, []);
return <button onClick={handleClick}>Click me</button>;
}
export default SimpleButton;
useMemo(重要度:中)
特徴
メモ化された値を生成するフック。
(値だけでなく関数など、なんでもメモ化できる。)
使用シチュエーション
-
計算コストが高い操作(フィルタリング、ソートなど)がある。
その実行結果をメモ化する。
(計算結果が変わらない場合は再実行不要→パフォーマンスが向上する)
サンプルコード
const sortedList = useMemo(() => sortList(list), [list]);
具体例
数字の二乗の計算結果をメモ化する。
import React, { useState, useMemo } from 'react';
function SquaredNumber() {
const [number, setNumber] = useState(0);
// useMemoを使用して数値の二乗を計算し、その結果をメモ化する。
// 依存配列にnumberを指定しているため、numberが変更されたときのみ再計算される。
const squared = useMemo(() => number * number, [number]);
return (
<div>
<input
type="number"
value={number}
onChange={e => setNumber(parseInt(e.target.value, 10))}
/>
<p>二乗した値: {squared}</p>
</div>
);
}
export default SquaredNumber
useRef(重要度:中)
特徴
特定のDOM要素や任意のデータ値への「リンク」(参照)を作成し、それを保持するためのフック。(getElementByIdのReact版)
これにより、DOM要素に直接アクセスしたり、コンポーネントの再レンダリングで変化しない値を保持することができる。
使用シチュエーション(DOM要素への直接アクセス)
- ボタンクリックで特定のテキスト入力フィールドにフォーカスを設定する。
- DOM要素に直接アニメーションを適用する。
- 特定のDOM要素のサイズや画面上の位置を計測する。
使用シチュエーション(コンポーネント内での値の保持)
- 再レンダリングされても変化しない値(例えば、setIntervalのID)を保持する。
- 以前のレンダリング時の状態やプロップの値を保存しておく。
- 複数のレンダリング間で共有される値を管理する。
サンプルコード
const inputRef = useRef();
具体例
初回レンダリング時、入力フォームにフォーカスを設定する。
import React, { useRef, useEffect } from 'react';
function AutoFocusedInput() {
// 参照を作成
const inputRef = useRef();
useEffect(() => {
// input要素への参照を使用して、その要素にフォーカスを当てる。
inputRef.current.focus();
}, []);
// input要素に参照を割り当てる
return <input ref={inputRef} type="text" />;
}
export default AutoFocusedInput;
カスタムフック(重要度:中)
特徴
自分で定義するフック。
use〇〇
という名前の独自の関数のことをカスタムフックという。
使用シチュエーション
他のフック(useState
, useEffect
など)を使った処理を関数化するときに使う。
- データ取得処理を関数化して複数コンポーネントで使い回す。
- アニメーションを関数化して複数コンポーネントで使い回す。
サンプルコード
function useHogeHook(){ … }
具体例
データ取得のカスタムフック(独自の関数)を定義
useFetchUserData.js
import { useState, useEffect } from 'react';
// ユーザーデータを取得するカスタムフック
function useFetchUserData() {
const [userData, setUserData] = useState(null);
useEffect(() => {
// fetchUserData() は外部APIからユーザーデータを取得する架空の関数
fetchUserData().then(data => setUserData(data));
}, []);
return userData;
}
export default useFetchUserData;
定義したカスタムフック(独自の関数)を使う
MyComponent.jsx
import React from 'react';
import useFetchUserData from './useFetchUserData'; // カスタムフックをインポート
// ユーザーデータを表示するコンポーネント
function MyComponent()
// カスタムフック(データ取得処理)を実行
const userData = useFetchUserData();
return (
<div>
<h1>User Data</h1>
<p>{JSON.stringify(userData)}</p>
</div>
);
}
export default MyComponent;
useImperativeHandle(重要度:低)
特徴
子コンポーネントから親コンポーネントへ特定の機能や値を提供するフック。
(親から子の関数などが使える)
使用シチュエーション
- 親コンポーネントから子コンポーネントのアニメーションを開始・停止する。
useImperativeHandle
は特殊なケースでのみ使用する。
Reactでは親コンポーネントから子コンポーネントのフローが基本。
サンプルコード
useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); } }));
具体例
親コンポーネントから子コンポーネントの関数を直接呼び出す。
import React, { useRef, useImperativeHandle, forwardRef } from 'react';
// 子コンポーネント
const ChildComponent = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
// 3. このメソッドは親コンポーネントから直接呼び出すことができる
customMethod() {
console.log('customMethodが呼び出されました');
}
}));
return <div>子コンポーネント</div>;
});
// 親コンポーネント
const ParentComponent = () => {
// 1. 子コンポーネントの参照を作成
const childRef = useRef();
const handleClick = () => {
// 4. 子コンポーネントのcustomMethodを直接呼び出す
childRef.current.customMethod();
};
return (
<div>
{/* 2. 子コンポーネントの参照を渡す */}
<ChildComponent ref={childRef} />
<button onClick={handleClick}>子コンポーネントのメソッドを呼び出す</button>
</div>
);
};
export default ParentComponent;
【補足】useImperativeHandle
はuseRef
と組み合わせて使う
以下の手順で使う。
-
親コンポーネントで
useRef
を使って参照(ref)を作成する。 - 参照を子コンポーネントに渡す。
-
子コンポーネント内で
useImperativeHandle
を使い、この参照を通じて親コンポーネントに公開したい関数や値を定義する。 - 親コンポーネントで子コンポーネントの関数や値を使う。
useLayoutEffect(重要度:低)
特徴
画面を再描画する直前に、副作用(DOM要素のサイズ計算など)を行うフック。
使用シチュエーション
- 描画前の特定の要素のサイズを計測し、それに基づいてレイアウトの調整を行う。
- 再描画時のちらつきを防ぐ。
サンプルコード
useLayoutEffect(() => { measurePosition(node); }, [node]);
具体例
コンポーネントのサイズを計測する。
import React, { useLayoutEffect, useRef, useState } from 'react';
function MeasureComponent() {
const [size, setSize] = useState({});
const ref = useRef();
// 同期的にブラウザの描画の前に実行される。
// そのため画面のちらつきを防ぐことができるがパフォーマンスが悪くなる。
useLayoutEffect(() => {
setSize({
width: ref.current.offsetWidth,
height: ref.current.offsetHeight
});
}, []);
return (
<div ref={ref}>
<p>Width: {size.width}px, Height: {size.height}px</p>
</div>
);
}
export default MeasureComponent;
useDebugValue(重要度:低)
特徴
カスタムフックのデバッグ用フック
使用シチュエーション
- 複雑なカスタムフックの処理をデバッグしたいとき
- 複数のインスタンスで同じカスタムフックを使っていて、各インスタンスの状態を個別に確認したいとき
サンプルコード
useDebugValue(isOnline ? 'Online' : 'Offline');
具体例
カスタムフックの変数の状態を確認する。
import React, { useState, useDebugValue } from 'react';
function useCustomHook() {
const [value, setValue] = useState('これは初期値です');
// React Developer Toolsにデバッグ情報を表示する。
// valueが設定されている場合は'値はセットされています'、設定されていない場合は'値はセットされていません'と表示する。
useDebugValue(value ? '値はセットされています' : '値はセットされていません');
return value;
}
function Test() {
const value = useCustomHook();
return <div>{value}</div>;
}
export default Test;
「React Developer Tools」で確認した結果
気になった点のメモ
useCallbackとuseMemoは何が違う?
✅メモ化する対象が異なる。
useCallback
- 関数をメモ化
useMemo
- 値や関数など任意のものをメモ化(汎用的)
useCallback
は関数のメモ化を簡単に書くためのもの。
【補足】useCallback
→useMemo
の書き換えが可能!
useCallbackで書いた場合
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
useMemoで書き換えた場合
const memoizedCallback = useMemo(() => {
return () => {
doSomething(a, b);
};
}, [a, b]);
とりあえず全部の変数や関数にuseCallback、useMemoを付けていい?
✅必要なとき(計算量が多いときなど)だけ使うのがベストだが、初心者のうちは全部つけてもOK。
そもそもuseMemo
、useCallback
自体にメモリと計算コストが伴うことは覚えておきたい。
全部にuseMemo、useCallbackを付けるメリット
-
理解の向上
実際に使ってみることで、これらのフックがどのように動作するかを深く理解できる。特に、依存関係を意識しながら開発できるのがメリット◎
-
パフォーマンスへの意識
常に
useMemo
やuseCallback
を使うことで、最適なパフォーマンスへの意識が高まる。
全部にuseMemo、useCallbackを付けるデメリット
-
パフォーマンスの低下の恐れ
useMemo
やuseCallback
自体にもメモリと計算コストが伴う。不必要な場合は逆にパフォーマンスが(少しだけ)悪くなる。 -
可読性の低下
コードが長くなる。初心者は特に読みづらく感じる可能性がある。
ただし逆にパフォーマンスが悪くなる可能性があることを覚えておく!
useEffectとuseLayoutEffectは何が違う?
✅実行されるタイミング、同期・非同期が異なる
useEffect
- レンダリングが完了した後
- 非同期的に実行される
useLayoutEffect
- ブラウザの描画前
- 同期的に実行される