アドベントカレンダーの時期が来まして...今年はReact Hooksをテーマにしていこうと思っております。
フロントエンドで開発するに当たってReact標準意外にもさまざまなライブラリがあり,何をどう使えばいいか正しく理解できていない方も多くいらっしゃるかと思います。
そこで、まずReactそのものから学ぶことが大事だと思うので全て紹介できたらと思います。
1日1記事でReact Hooksの基本的な使い方を紹介していきます。
Reactとは?
Reactは、ユーザーインターフェース(UI)を構築するためのJavaScriptライブラリです。Facebook(現Meta)によって開発され、現在はオープンソースとして広く利用されています。
Reactの主な特徴:
- コンポーネントベース: UIを再利用可能なコンポーネントに分割して構築
- 宣言的UI: データの状態に基づいてUIを記述し、Reactが自動的に更新を管理
- 仮想DOM: 効率的な更新のために仮想DOMを使用し、パフォーマンスを最適化
- 単方向データフロー: データが親から子へ一方向に流れる明確な構造
DOMに関してはこちらの記事から
フック(Hooks)とは何か?
フック(Hooks)は、React 16.8で導入された機能で、関数コンポーネントでstateやライフサイクルなどのReactの機能を使えるようにするものです。
フックの主な目的
- 関数コンポーネントの強化: クラスコンポーネントを使わずに、関数コンポーネントでReactの機能を利用可能に
- ロジックの再利用: カスタムフックを作成することで、コンポーネント間でロジックを共有
- コードの簡潔性: クラスコンポーネントよりも簡潔で読みやすいコードを記述可能
フックのルール
- フックは関数コンポーネントのトップレベルでのみ呼び出す
- 条件分岐やループの中でフックを呼び出してはいけない
- カスタムフックは
useで始まる名前を付ける
Reactには、様々な用途に応じた組み込みフックが用意されています。以下、カテゴリ別に説明します。
State フック
stateを使うと、ユーザーの入力などの情報をコンポーネントに「記憶」させることができます。
useState
直接的に更新できるstate変数を定義します。
const [count, setCount] = useState(0);
用途: フォームの入力値、モーダルの開閉状態、カウンターなど
useReducer
リデューサ関数内に書いたロジックを用いて更新を行うstate変数を定義します。
const [state, dispatch] = useReducer(reducer, initialState);
用途: 複雑なstateロジック、複数のstateをまとめて管理する場合
コンテクストフック
コンテクストを用いると、コンポーネントはpropsを渡すことなく、離れた親要素から情報を取得できるようになります。
useContext
コンテクストの値を読み取り、変更を受け取れるようにします。
const theme = useContext(ThemeContext);
用途: テーマ、認証情報、言語設定など、複数のコンポーネントで共有するデータ
Ref フック
refを用いると、コンポーネントはDOMノードやタイムアウトIDなどの、レンダーに用いない情報を保持することができます。stateと違い、refの値を更新してもコンポーネントは再レンダーされません。
useRef
refを宣言します。useRefにはどんな値でも格納できますが、多くの場合、DOMノードを格納するために使われます。
const inputRef = useRef(null);
用途: DOM要素への直接アクセス、タイマーIDの保持、前回の値を保持
useImperativeHandle
コンポーネントが公開するrefをカスタマイズできます。これはほとんど用いられることはありません。
useImperativeHandle(ref, () => ({
focus: () => { /* ... */ }
}));
用途: 親コンポーネントから子コンポーネントの特定のメソッドを呼び出す場合
エフェクトフック
エフェクトを使うことで、コンポーネントを外部システムに接続し、同期させることができます。これには、ネットワーク、ブラウザのDOM、アニメーション、別のUIライブラリを使って書かれたウィジェット、その他の非Reactコードの処理が含まれます。
useEffect
外部のシステムとコンポーネントを接続します。
useEffect(() => {
const connection = createConnection(roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId]);
用途: API呼び出し、イベントリスナーの登録、タイマーの設定、外部ライブラリとの連携
useLayoutEffect
ブラウザが画面を再描画する前に発火します。このフックでレイアウトを測定できます。
useLayoutEffect(() => {
// DOMの測定や同期的な更新
}, [dependencies]);
用途: DOMのサイズや位置の測定、同期的なDOM操作が必要な場合
useInsertionEffect
ReactがDOMに変更を加える前に発火します。ライブラリは動的なCSSをこのフックで挿入できます。
useInsertionEffect(() => {
// スタイルの挿入
}, [dependencies]);
用途: CSS-in-JSライブラリでのスタイル注入
useEffectEvent
エフェクト内で使用するイベントハンドラを定義します。依存配列に含めずに最新のpropsやstateにアクセスできます。
const onConnected = useEffectEvent(() => {
showNotification(roomId, theme);
});
用途: エフェクト内で最新のpropsやstateを参照したいが、依存配列に追加したくない場合
依存配列とは
依存配列とは、エフェクト内で使用するpropsやstateを依存配列に追加することで、エフェクト内で使用するpropsやstateが変更されたときにエフェクトを再実行することができるものです。
例えば、以下のようなエフェクトがあるとします。
useEffect(() => {
console.log(count);
}, [count]);
このエフェクトは、countが変更されたときに再実行されます。
しかし、以下のようなエフェクトがあるとします。
useEffect(() => {
console.log(count);
}, [count]);
このエフェクトは、countが変更されたときに再実行されません。
なぜなら、依存配列にcountが含まれていないからです。
useEffect(() => {
console.log(count);
}, []);
このエフェクトは、countが変更されたときに再実行されません。
useEffect(() => {
console.log(count);
}, [count]);
このエフェクトは、countが変更されたときに再実行されます。
useEffect(() => {
console.log(count);
}, [count]);
このエフェクトは、countが変更されたときに再実行されます。
パフォーマンス関連フック
再レンダーのパフォーマンスを最適化するためのよくある方法は、不要な処理を減らすことです。
useMemo
高負荷な計算の結果をキャッシュできます。
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
用途: 重い計算処理の結果をメモ化、参照の安定化
useCallback
最適化済みのコンポーネントに渡すために関数定義をキャッシュしておくことができます。
const handleClick = useCallback(() => {
// ...
}, [dependencies]);
用途: 子コンポーネントに渡すコールバック関数のメモ化
useTransition
stateの遷移をノンブロッキングなものとしてマークし、他の更新による割り込みを許可します。
const [isPending, startTransition] = useTransition();
用途: ユーザーインターフェースをブロックしない更新、検索結果の表示など
useDeferredValue
UIの重要でない部分の更新を遅延させて、他の部分を先に更新させることができます。
const deferredValue = useDeferredValue(value);
用途: 検索入力の即時反映と結果表示の遅延、リストのフィルタリング
useOptimistic
楽観的更新(optimistic update)を実装するためのフックです。UIを即座に更新し、バックエンドの応答を待たずにユーザーにフィードバックを提供します。
const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);
用途: いいねボタン、コメント投稿、フォーム送信など、即座のフィードバックが重要な操作
その他のフック
これらのフックはライブラリの開発者には有用ですが、アプリケーションコードでは通常は用いられることはありません。
useDebugValue
React DevToolsが表示するカスタムフックのラベルをカスタマイズできます。
useDebugValue(value, formatValue);
用途: カスタムフックのデバッグ情報を表示
useId
コンポーネントにユニークなIDを関連付けることができます。通常はアクセシビリティAPIとともに使用されます。
const id = useId();
用途: フォームのラベルと入力の関連付け、アクセシビリティ属性
useSyncExternalStore
コンポーネントは外部のストアを参照できるようになります。
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
用途: 外部の状態管理ライブラリとの統合
useActionState
アクションのstateを管理できます。フォーム送信などの非同期アクションの状態を扱います。
const [state, formAction, isPending] = useActionState(action, initialState);
用途: フォーム送信、サーバーアクションの状態管理
React Hooksは、関数コンポーネントでReactの強力な機能を利用するための仕組みです。各フックには特定の用途があり、適切に組み合わせることで、効率的で保守性の高いコンポーネントを構築できます。
-
State管理:
useState,useReducer -
コンテクスト:
useContext -
DOM操作:
useRef,useImperativeHandle -
副作用処理:
useEffect,useLayoutEffect,useInsertionEffect,useEffectEvent -
パフォーマンス最適化:
useMemo,useCallback,useTransition,useDeferredValue,useOptimistic -
その他:
useDebugValue,useId,useSyncExternalStore,useActionState
DAY2から、各フックの詳細な使い方と実践的な例を紹介します。
DAY2
鋭意執筆中
