React 面接の質問
React面接では、コンポーネントモデル、フック、レンダリング動作、パフォーマンスの理解度が問われます。概念的な質問と小さなライブコーディング課題が出題されます。
React 面接で問われる内容
フックと状態
useState、useEffect、useMemo/useCallback、カスタムフック、フックのルール。
レンダリングとリコンシリエーション
Reactの再レンダリング、キー、仮想DOM、不要なレンダリングの回避。
状態管理
ローカル状態とリフトアップ状態、コンテキスト、Redux・Zustand・React Queryの使い分け。
パフォーマンス
メモ化、コード分割、遅延ローディング、遅いコンポーネントのプロファイリング。
React 面接の質問例
- Reactフックはどのような問題を解決し、そのルールは何ですか?良い回答が押さえる点
- クラスコンポーネントの複雑さ(this、ライフサイクル)の解消
- 状態ロジックの再利用と構成の容易化
- ルール:フックはトップレベルでのみ呼び出す(ループや条件文の内部では不可)
- ルール:React関数コンポーネントまたはカスタムフック内でのみ呼び出す
サンプル回答を見る
Reactフックは、クラスコンポーネントにおけるthisの曖昧さやライフサイクルメソッドの煩雑さを解消し、状態や副作用のロジックを関数コンポーネント内で簡潔に記述できるようにします。また、カスタムフックにより状態ロジックをコンポーネント間で再利用可能にし、構成を容易にします。フックには二つのルールがあります。第一に、フックはコンポーネントのトップレベルでのみ呼び出すこと。これにより、レンダリングごとに同じ順序でフックが呼ばれることが保証されます。第二に、フックはReact関数コンポーネントまたはカスタムフックの中でのみ呼び出すこと。これにより、Reactの状態管理機能が正しく動作します。これらのルールを破ると、バグや予期しない動作の原因となります。
- useMemoとuseCallbackの違いと、それぞれを使うべき場面を説明してください。良い回答が押さえる点
- useMemoは計算結果をメモ化、useCallbackは関数自体をメモ化
- useMemo:高コストな計算やオブジェクトの参照同一性が必要な場合
- useCallback:子コンポーネントにコールバックを渡す際の不要な再レンダリング防止
- 両者の依存配列が同じであれば、useCallbackはuseMemoの糖衣構文
サンプル回答を見る
useMemoは計算結果の値をメモ化し、依存配列が変わらない限り再計算をスキップします。一方、useCallbackは関数自体をメモ化します。使用場面として、useMemoは大きな配列のフィルタリングやソートなど高コストな計算、またはReact.memoと組み合わせて子コンポーネントに渡すオブジェクトの参照を安定させる場合に適しています。useCallbackは、React.memoでラップされた子コンポーネントにコールバック関数を渡す際、依存配列が変わらない限り同じ関数参照を維持して不要な再レンダリングを防ぐために使います。実質的にuseCallback(fn, deps)はuseMemo(() => fn, deps)と等価です。誤った依存配列や過剰な使用は逆効果になるため、パフォーマンス計測に基づき使用すべきです。
- コンポーネントが再レンダリングされる原因と、不要な再レンダリングを防ぐ方法は?良い回答が押さえる点
- 再レンダリングの原因:state変更、props変更、親コンポーネントの再レンダリング
- React.memoによるコンポーネントのメモ化
- useMemo/useCallbackによる値・関数のメモ化
- useContextの分割や状態の局所化
- リストのkeyにインデックスを使わない
サンプル回答を見る
コンポーネントが再レンダリングされる主な原因は、自身のstateが変更されたとき、親から渡されるpropsが変更されたとき、または親コンポーネント自体が再レンダリングされたときです。不要な再レンダリングを防ぐ方法としては、まずReact.memoを使用して、propsが変更されていないコンポーネントの再レンダリングをスキップします。次に、useMemoやuseCallbackで高コストな計算やコールバックをメモ化し、子コンポーネントに渡す値を安定させます。また、Contextを使う場合は、状態を細かく分割して不要なコンポーネントに影響が及ばないようにします。さらに、リストのkeyには安定した一意の値(データベースのIDなど)を使用し、インデックスを使わないことで、再レンダリング時の差分検出が効率的になります。これらを組み合わせることで、再レンダリングを最小限に抑えられます。
- リストにおけるkeyプロパティの仕組みと、その重要性は?良い回答が押さえる点
- keyはReactが要素の識別と変更検出に使用する
- keyが同一なら要素を再利用、変更ならマウント/アンマウント
- リストの差分計算(reconciliation)を最適化
- keyにインデックスを使うとバグの原因(要素削除時の状態混同)
- 一意で安定した値(IDなど)を使用すべき
サンプル回答を見る
keyプロパティは、Reactが仮想DOMの差分計算(reconciliation)を行う際に、どの要素が変更・追加・削除されたかを識別するために使用されます。keyが同じであればReactはその要素を再利用し、異なれば新しい要素としてマウントします。これにより、リスト内の順序が変わっても効率的に更新できます。重要性は、keyを適切に設定しないとレンダリングパフォーマンスが低下し、特にリストの先頭や途中に要素が挿入・削除される場合に、不必要なDOM操作が発生する点です。また、keyに配列のインデックスを使うと、要素が削除された際に状態が誤って保持されるなど、バグの原因になります。そのため、一意で安定した値(データベースのIDなど)をkeyに指定するべきです。
- カスタムフックを使ったデバウンス検索入力を実装してください。良い回答が押さえる点
- カスタムフックによるロジックの抽出
- useStateとuseEffectの組み合わせ
- useRefでタイマーIDを保持
- クリーンアップでタイマー解除
- 依存配列にキーワードを指定
サンプル回答を見る
デバウンス検索入力を実装するカスタムフックを作成します。このフックは、ユーザーが入力を止めてから一定時間後に検索を実行するロジックを提供します。内部ではuseStateで入力値と検索結果を管理し、useEffectでsetTimeoutを用いてデバウンス処理を行います。入力が変更されるたびに前回のタイマーをクリアし、新しいタイマーを設定します。クリーンアップ関数でタイマーを解除することでメモリリークを防ぎます。また、useRefでタイマーIDを保持することで、レンダリング間で一貫した参照を保ちます。以下のコードが実装例です。
参考コードjavascript import { useState, useEffect, useRef } from 'react'; /** * デバウンス検索用カスタムフック * @param {string} keyword 検索キーワード * @param {number} delay デバウンス遅延時間(ミリ秒) * @param {Function} searchFn 実際の検索関数(非同期) * @returns {object} { result, loading, error } */ function useDebouncedSearch(keyword, delay, searchFn) { const [result, setResult] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const timerRef = useRef(null); // タイマーIDを保持 useEffect(() => { // 前回のタイマーをクリア if (timerRef.current) { clearTimeout(timerRef.current); } // キーワードが空の場合は検索しない if (!keyword.trim()) { setResult(null); setLoading(false); return; } setLoading(true); setError(null); // 新しいタイマーを設定 timerRef.current = setTimeout(async () => { try { const data = await searchFn(keyword); setResult(data); } catch (err) { setError(err); } finally { setLoading(false); } }, delay); // クリーンアップ関数:コンポーネントアンマウント時やkeyword変更時にタイマー解除 return () => { if (timerRef.current) { clearTimeout(timerRef.current); } }; }, [keyword, delay, searchFn]); // 依存配列にsearchFnを含める(安定した参照を推奨) return { result, loading, error }; } export default useDebouncedSearch; - ContextとReduxやReact Queryのような状態管理ライブラリは、どのような場合に使い分けますか?良い回答が押さえる点
- Context:静的なテーマやロケールなど、変更頻度が低くツリー全体で必要なデータ
- Redux:大規模アプリで複雑な状態ロジック、ミドルウェア、DevToolsが必要な場合
- React Query:サーバー状態のキャッシュ、同期、リフェッチに特化
- Contextは更新時に全Consumerを再レンダリングさせやすいため注意
- 小規模~中規模ではContext+useReducerで十分なことも多い
サンプル回答を見る
Contextは、プロップドリルを回避するための組み込み機能で、テーマやロケールなど比較的変化が少なく、アプリケーション全体で共有される静的なデータに向いています。一方、Reduxは大規模アプリケーションで、複雑な状態ロジックや非同期処理、ミドルウェア(例:redux-saga)、強力なDevToolsが必要な場合に適しています。React Queryはサーバー状態(APIからのデータ)の管理に特化しており、キャッシュ、自動リフェッチ、楽観的更新などを簡単に実装できます。使い分けの基準として、Contextは小規模でシンプルな状態共有に、Reduxは厳格な状態管理とデバッグが必要な大規模プロジェクトに、React Queryはサーバーデータの同期処理にそれぞれ適しています。Contextは更新時に全Consumerが再レンダリングされるため、パフォーマンスに注意が必要です。また、ContextとuseReducerを組み合わせることで、中規模アプリでは十分に状態管理が可能な場合もあります。
準備方法
- レンダリングについて声に出して説明できるようにしましょう — 面接官はAPIの暗記よりも思考プロセスを重視します。
- 小さなライブコーディング課題(カウンター、デバウンス入力、リスト取得)を自動化できるまで練習しましょう。
- 一つの方法に固執せず、各状態管理ツールのトレードオフを理解しましょう。
- 最適化の議論ではパフォーマンスツール(React DevTools Profiler)に言及しましょう。
よくある質問
React面接の質問は理論とコーディングのどちらが中心ですか?
両方です。フックやレンダリングに関する概念的な質問と、小さなコンポーネントを構築する短いライブコーディング課題が予想されます。
React面接にReduxは必要ですか?
トレードオフと状態管理ライブラリが役立つ場面を理解しましょう。ただし、多くのチームはContext + React Queryを好みます。一つのライブラリを暗記するよりも、その理由を理解することが重要です。
TypeScriptはどの程度のレベルが求められますか?
ほとんどのReact職では、props、state、フックの型付けに慣れていることが期待されます。シニアやインフラ関連の職でなければ、高度なジェネリクスはほとんど要求されません。
React面接の練習方法は?
時間制限のあるライブコーディングを行い、考え方を声に出して説明しましょう。Offerslyは履歴書からReact関連の質問を生成し、回答を採点します。
React の質問をAIで練習、瞬時にフィードバック
履歴書をアップロードして、パーソナライズされた模擬面接を受け、改善点を確認 — 無料で始められます。