Fast Refresh
Fast Refresh は Next.js の機能で、React コンポーネントの編集に対して即座にフィードバックを提供します。Fast Refresh は バージョン 9.4 以降 のすべての Next.js アプリケーションでデフォルトで有効になっています。Next.js の Fast Refresh が有効な場合、ほとんどの編集は コンポーネントの状態を失うことなく、1秒以内に反映されます。
動作の仕組み
- React コンポーネントのみをエクスポートするファイル を編集した場合、Fast Refresh はそのファイルのコードのみを更新し、コンポーネントを再レンダリングします。スタイル、レンダリングロジック、イベントハンドラ、エフェクトなど、そのファイル内のあらゆる部分を編集できます。
- React コンポーネントでないものをエクスポートするファイルを編集した場合、Fast Refresh はそのファイルと、それをインポートしている他のファイルの両方を再実行します。例えば、
Button.js
とModal.js
の両方がtheme.js
をインポートしている場合、theme.js
を編集すると両方のコンポーネントが更新されます。 - 最後に、React ツリー外のファイルからインポートされているファイルを編集した場合、Fast Refresh は フルリロードにフォールバック します。例えば、React コンポーネントをレンダリングするが、非 React コンポーネントからインポートされる値もエクスポートするファイルがある場合です。このような場合、定数を別のファイルに移動し、両方のファイルからインポートすることを検討してください。これにより Fast Refresh が再び機能するようになります。他のケースも通常は同様の方法で解決できます。
エラーへの耐性
構文エラー
開発中に構文エラーを発生させた場合、ファイルを修正して再度保存するだけでエラーは自動的に消えます。アプリをリロードする必要はありません。コンポーネントの状態は失われません。
ランタイムエラー
コンポーネント内でランタイムエラーを引き起こすミスをした場合、コンテキストに応じたオーバーレイが表示されます。エラーを修正すると、アプリをリロードすることなくオーバーレイが自動的に消えます。
レンダリング中にエラーが発生しなかった場合、コンポーネントの状態は保持されます。レンダリング中にエラーが発生した場合、React は更新されたコードを使用してアプリケーションを再マウントします。
アプリにエラーバウンダリがある場合(本番環境での優雅な失敗のために推奨)、レンダリングエラーの後の次の編集でレンダリングを再試行します。つまり、エラーバウンダリがあると、常にルートアプリの状態にリセットされるのを防げます。ただし、エラーバウンダリを過度に細かく設定しないように注意してください。これらは本番環境で React によって使用され、常に意図的に設計されるべきです。
制限事項
Fast Refresh は編集中のコンポーネントのローカル React 状態を保持しようとしますが、安全な場合に限ります。ファイルを編集するたびにローカル状態がリセットされる可能性がある理由は次のとおりです:
- クラスコンポーネントではローカル状態は保持されません(関数コンポーネントと Hooks のみ状態を保持します)。
- 編集しているファイルに React コンポーネント以外のエクスポートがある場合。
- 場合によっては、ファイルが
HOC(WrappedComponent)
のような高階コンポーネントの呼び出し結果をエクスポートすることがあります。返されるコンポーネントがクラスの場合、その状態はリセットされます。 export default () => <div />;
のような無名アロー関数は、Fast Refresh がローカルコンポーネント状態を保持しない原因になります。大規模なコードベースではname-default-component
codemod を使用できます。
コードベースの多くが関数コンポーネントと Hooks に移行するにつれて、より多くのケースで状態が保持されるようになります。
ヒント
- Fast Refresh はデフォルトで関数コンポーネント(および Hooks)の React ローカル状態を保持します。
- 時には、状態を強制的にリセットし、コンポーネントを再マウントしたい場合があります。例えば、マウント時にのみ発生するアニメーションを調整する場合などです。これを行うには、編集しているファイルの任意の場所に
// @refresh reset
を追加します。このディレクティブはファイルローカルで、Fast Refresh にそのファイルで定義されたコンポーネントを毎回編集ごとに再マウントするよう指示します。 - 開発中に編集するコンポーネントに
console.log
やdebugger;
を入れることができます。 - インポートは大文字と小文字を区別することを忘れないでください。インポートが実際のファイル名と一致しない場合、高速リフレッシュとフルリフレッシュの両方が失敗する可能性があります。例えば、
'./header'
と'./Header'
など。
Fast Refresh と Hooks
可能な場合、Fast Refresh は編集間でコンポーネントの状態を保持しようとします。特に、useState
と useRef
は、引数や Hook の呼び出し順序を変更しない限り、以前の値を保持します。
useEffect
、useMemo
、useCallback
などの依存関係を持つ Hook は、Fast Refresh 中に常に更新されます。Fast Refresh が行われている間、依存関係のリストは無視されます。
例えば、useMemo(() => x * 2, [x])
を useMemo(() => x * 10, [x])
に編集すると、x
(依存関係)が変更されていなくても再実行されます。React がこれをしなければ、編集が画面に反映されないでしょう!
時には、これが予期しない結果を招くことがあります。例えば、依存関係の空配列を持つ useEffect
でさえ、Fast Refresh 中に一度再実行されます。
しかし、useEffect
が時折再実行されることに耐えられるコードを書くことは、Fast Refresh がなくても良い習慣です。後で新しい依存関係を導入しやすくなり、React Strict Mode によって強制されます。React Strict Mode の有効化を強くお勧めします。