(最終更新月:2023年6月)
✔このような方へ向けて書かれた記事となります
「React Hooksってどんな特徴があるのだろうか?」
「React Hooksの使い方を習得したい」
「具体的なReact Hooksの実装例が知りたい」
✔当記事を通じてお伝えすること
- React Hooksの基本概念
- React Hooksの使い方とその活用方法
- React Hooksを用いた実践的な例
当記事では、React Hooksの基本的な理解から、効果的な活用方法まで、実際のコード例を交えてわかりやすく解説しています。
ぜひ最後までお読みいただき、React Hooksの魅力を存分に感じてください。
React Hooksの概要
本章では、React Hooksについての基本的な概念を紹介します。
- React Hooksとは
- 特徴・メリット
React Hooksとは
React Hooksは、React 16.8から導入された新機能。
関数コンポーネントで、ステート管理やライフサイクルメソッドの利用を可能にします。
たとえばライフサイクルメソッドは、以下のようなものです。
- constructor():初期のstateの設定やイベントハンドラのバインドをおこなう
render()
:コンポーネントがどのように見えるべきかを定義- componentDidMount():コンポーネントがDOMに挿入された後に呼び出される
- componentDidUpdate():コンポーネントの更新・再レンダリングが終わった後に呼び出される
- componentWillUnmount():コンポーネントがDOMから削除される直前に呼び出される
クラスコンポーネントだけでなく、よりシンプルな関数コンポーネントでもこれらの機能を活用できるようになったのです。
特徴・メリット
React Hooksの特徴は、関数コンポーネントでもステート管理やライフサイクル管理ができる点です。
メリットはこちら。
- コードの量が少ない
- 読みやすくメンテナンスしやすい
- コンポーネント間でロジックを再利用しやすい
- コードの再利用率を上げられる
これらのメリットにより、React Hooksは多くの開発者にとって有用なツールとなっています。
基本的なReact Hooksの使い方
React Hooksの中にはさまざまな種類があり、それぞれ異なる機能を提供します。
よく使われる基本的なHooksについて詳しく見ていきましょう。
- useState:ステート管理
- useEffect:ライフサイクル管理
- useContext:コンテクストの利用
- useRef:参照の作成
- useMemo:メモ化による最適化
- useCallback:イベントハンドラの最適化
- useReducer:複雑なステート管理
- 基本Hooksの使い方まとめ
useState:ステート管理
useStateは、関数コンポーネントでステートを使うためのHooksです。
ステートの現在値と、その値を更新するための関数ペアを返します。
const [変数名, 変数に値を入れる関数名] = useState(初期値);
引数としてステートの初期値を取り、その初期値を元にステートを初期化します。
import React, { useState } from 'react';
function Counter() {
// stateの初期化
const [count, setCount] = useState(0);
return (
Count: {count}
<button onClick={()=> () => setCount(count + 1)}>Increase</button>
</div>
);
}
上記は以下のようなコードになっています。
- 初期値が0
- ステートを管理する変数名は、「count」
- setCount(新しい値)で、count内の変数を更新
ボタンをクリックするとsetCount
関数が呼ばれ、count
の値が増える仕組みです。
useEffect:ライフサイクル管理
useEffectは、データなどの変更を画面に反映させるための関数です。
専門的にいえば、副作用(サイドエフェクト)を管理するためのHooksといわれます。
useEffectが発動するのは、以下のタイミングです。
- コンポーネント(ページなど)の初回読み込み(レンダリング)
- 指定した値に変更があったとき
初回読み込みの場合
初回読み込みは、以下のようになります。
useEffect(() => {
//初回読み込みのときにおこないたい処理を記述
},[]}
リスト([ ])が空であることがポイント!
指定した値に変更があったとき
変更を反映させたい値は、リスト([ ] )の中にその変数を入れましょう。
全体を見ると以下のようになります。
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 副作用の実行
useEffect(() => {
document.title = You clicked ${count} times;
},[count]);
return (
You clicked {count} times setCount(count + 1)}> Click me
);
}
[count]となっているのがわかりますでしょうか。
count変数が変更する度に画面が再読込みされ、値の変更が反映されるのです。
useContext:コンテクストの利用
useContextは、ReactのContext APIを関数コンポーネントで使うためのHooksです。
これを使用すると、コンポーネントツリー全体で共有したいデータを、そのツリー内のどの深さにもあるコンポーネントに直接渡せます。
import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <p>Theme: {theme}</p>;
}
上記の例では、以下のようになっています。
ThemeContext
が作成され、そのデフォルト値がlight
と設定- useContext(ThemeContext)により、そのコンテクトの現在の値が取得できる
- このThemedButtonコンポーネントがレンダリングされる際のThemeContextの値に基づく
useRef:参照の作成
useRefは、ReactのRefを関数コンポーネントで使うためのHooksです。
このHooksを用いることで、DOM要素への参照を保持したり、コンポーネント間で値を保持したりできます。
一度作成されたrefオブジェクトは、そのライフサイクルを通じて保持されます。
import React, { useRef } from 'react';
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` ポインタを使ってテキスト入力エレメントにアクセス
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
useRefを使用してinput要素への参照(ref)を作成し、そのrefをinputElに保存。
ボタンがクリックされたときに、このrefを使ってinput要素にフォーカスを当てるようにしています。
useMemo:メモ化による最適化
useMemoは、関数の返り値をメモ化(記憶)するためのHooksです。
これにより、同じ引数で何度も関数が呼ばれるときに、前回計算した結果を再利用できます。
これは計算コストの高い関数の実行時間を節約したり、レンダリング間で値を保持することを可能にします。
import React, { useState, useMemo } from 'react';
function fibonacci(num) {
if (num <= 1) return 1;
return fibonacci(num - 1) + fibonacci(num - 2);
}
export default function FibonacciCalculator() {
const [number, setNumber] = useState(1);
const fibNumber = useMemo(() => fibonacci(number), [number]);
return (
<div>
<h1>Fibonacci Calculator</h1>
<p>
Fibonacci of {number} is {fibNumber}
</p>
<button onClick={() => setNumber(number + 1)}>
Increase
</button>
<button onClick={() => setNumber(number - 1)}>
Decrease
</button>
</div>
);
}
このコードでは、useMemo
を用いてfibNumber
をメモ化しています。
このuseMemo
は、number
の値が変わるたびにfibonacci(number)
を実行し、その結果を返すもの。
もしnumber
の値が変わらなければ、useMemo
は前回計算した結果を再利用します。
同じ引数でfibonacci
関数を何度も呼び出す際の計算コストを節約できるのです。
useCallback:イベントハンドラの最適化
useCallbackは、関数そのものをメモ化するHooksです。
特にイベントハンドラなど、関数が依存関係として頻繁に使われる場合に有効です。
このHooksを用いることで、依存配列の値が変更されない限り、同一の関数を再利用が可能です。
import React, { useState, useCallback } from 'react';
export default function CallbackComponent() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount(count => count + 1);
}, []);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>Increment</button>
</div>
);
}
useCallbackを用いてincrement関数をメモ化するコードです。
increment関数の依存配列は空なので、この関数は一度だけ作成され、それ以降は再利用されます。
useReducer:複雑なステート管理
useReducerは、より複雑なステート管理を行うためのHooksです。
このHooksはReduxのような状態管理パターンを採用しています。
useReducerは、現在のステートとディスパッチメソッドをペアで返します。
import React, { useReducer } from 'react';
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
export default function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</div>
);
}
useReducerを使ってカウンターの状態を管理するサンプルコード。
useReducerは、現在の状態(state)と、その状態を更新するためのディスパッチメソッド(dispatch)をペアで返します。
このディスパッチメソッドは、アクションオブジェクトを引数に取り、レデューサーに送信。
レデューサーはそのアクションに基づいて新しい状態を返すという仕組みです。
基本Hooksの使い方まとめ
これまでに紹介した各Hooksにより、Reactの関数コンポーネントの柔軟性と再利用性を大幅に向上させられます。
- ステート管理
- ライフサイクル管理
- 参照の作成
- 関数の最適化
それぞれのHooksが提供する機能を理解し、適切に使い分けることが、効果的なReact開発の鍵となるでしょう。
便利なReact Hooks
基本的なReact Hooksのほかにも、さまざまな便利なカスタムHooksが提供されています。
これらのHooksを用いることで、特定の問題を解決するための解決策をさらに簡潔に、効率的に記述することができます。
- react-hook-form:フォーム管理
- formik
- カスタムHooks:独自のHooksの作成
react-hook-form:フォーム管理
react-hook-form
は、フォームのバリデーションとサブミットを容易におこなうためのライブラリ。
独自のHooksが提供されています。
このライブラリを使用することで、フォームフィールドのエラー管理やフォームのサブミットに関するロジックをシンプルに保てるのです。
formik:フォームバリデーションライブラリ
formikは、フォームのバリデーションなどを簡単におこなえるライブラリ。
useFormikというカスタムフックが用意されています。
一度覚えると、簡単にフォームが実装できるので、私も愛用しています。
カスタムHooks:独自のHooksの作成
React Hooksは、カスタムHooksの作成も可能。
独自の関数コンポーネント内で、再利用可能な状態フルなロジックが作れます。
カスタムHooksは、あなたのアプリケーションで特定の問題を解決するための共有可能なロジックを作成するのに役立つでしょう。
サンプルとして、ウィンドウの幅と高さを返すカスタムフック「useWindowDimensions」を紹介します。
import { useState, useEffect } from 'react';
function useWindowDimensions() {
const [width, setWidth] = useState(window.innerWidth);
const [height, setHeight] = useState(window.innerHeight);
useEffect(() => {
const handleResize = () => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
}
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
}
}, []);
return { width, height };
}
export default useWindowDimensions;
使い方は以下のとおり。
import React from 'react';
import useWindowDimensions from './useWindowDimensions';
function Component() {
const { width, height } = useWindowDimensions();
return (
<div>
<h1>Window dimensions</h1>
<p>Width: {width}px</p>
<p>Height: {height}px</p>
</div>
);
}
export default Component;
注意点と実践例
React Hooksを使用する際には、いくつかの注意点があります。
また、具体的な実践例を通じて、より深い理解を得ることが重要です。
- React Hooksの注意点
- クリーンアップ処理について
- クリックでタイトルが変更されるコンポーネント(クラスvs関数)
React Hooksの注意点
React Hooksの注意点を、事前に把握しておきましょう。
なぜならアプリケーション開発では、あとからの変更は全体に影響を与え、リスクがあるからです。
事前に理解を深めておくことで、あとからの変更を最小限に抑える必要があります。
具体的にはこちら。
- 関数のトップレベルでのみ呼び出すこと:条件文やループ、ネストした関数内でHookを呼び出してはいけません。これにより複数のuseStateとuseEffect呼び出しが各レンダー間で正しく関連付けられます。
- Reactの関数コンポーネント内でのみ呼び出すこと: Hookを通常のJavaScript関数内で呼び出さないでください。代わりに、それらを関数コンポーネントの本体内で直接呼び出せます。
クリーンアップ処理について
特にuseEffect
を使用する際には、クリーンアップ処理が必要となる場合があります。
クリーンアップ処理とは、副作用により設定された何らかの状態(例えばタイマーの設定やAPIの購読など)をコンポーネントがアンマウントされるときや、再レンダリングが発生する前に削除・停止するための処理です。
useEffect
内で関数を返すことで、このクリーンアップ処理を定義できます。
import React, { useState, useEffect } from 'react';
function TimerComponent() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const intervalId = setInterval(() => {
setSeconds(seconds => seconds + 1);
}, 1000);
// クリーンアップ関数を返す
return () => {
clearInterval(intervalId);
};
}, []); // 空の依存配列を指定して、エフェクトがマウントとアンマウント時にのみ実行されるようにします。
return <div>{seconds} seconds have passed since the component mounted.</div>
}
export default TimerComponent;
useEffect 内で定義した関数が、クリーンアップ関数(ここでは clearInterval(intervalId);)を返しています。
このクリーンアップ関数は、コンポーネントがアンマウントされるときに呼び出され、setInterval によって開始されたタイマーをクリアします。
不要なタイマーが動作を続けることを防ぎ、リソースの無駄を避けられるのです。
クリックでタイトルが変更されるコンポーネント(クラスvs関数)
クリックするとタイトルが変更されるコンポーネントを、クラスベースのコンポーネントと関数ベースのコンポーネントの両方で実装し、その違いを比較します。
この例を通じて、React Hooksの使い方やその有用性を実感していただければと思います。
クラスベースのコード
import React from 'react';
class ClassComponent extends React.Component {
constructor(props) {
super(props);
this.state = { title: 'Hello, World!' };
}
handleClick = () => {
this.setState({ title: 'Hello, React!' });
};
render() {
return (
<div>
<h1>{this.state.title}</h1>
<button onClick={this.handleClick}>Change Title</button>
</div>
);
}
}
export default ClassComponent;
関数ベースのコード
import React, { useState } from 'react';
function FunctionComponent() {
const [title, setTitle] = useState('Hello, World!');
const handleClick = () => {
setTitle('Hello, React!');
};
return (
<div>
<h1>{title}</h1>
<button onClick={handleClick}>Change Title</button>
</div>
);
}
export default FunctionComponent;
まとめ
React Hooksについて基本的な概念から、具体的な使い方、注意点などを学んできました。
React Hooksは、Reactの関数コンポーネントでステートとライフサイクルを扱うための強力なツールです。
各Hooksは特定の目的に特化し、多岐にわたるタスクを効率的に処理します。
- コンポーネントの状態管理
- 副作用の制御
- メモ化
- 参照の取得
React Hooksの理解を深めるためには、公式ドキュメントやオンラインのチュートリアル、書籍などのリソースを活用することをおすすめします。
自分でプロジェクトを作成し、実際にHooksを使用してみることで、より深い理解とスキルを身につけましょう。