Fast Refresh
Fast Refresh は Next.js に統合された React の機能で、ファイル変更を保存した際にブラウザページをライブリロードしながら、クライアントサイドの一時的な状態を保持します。この機能は バージョン9.4以降 のすべての 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)の呼び出し結果(例:
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 呼び出しの順序を変更しない限り、以前の値を保持します。
依存関係を持つ Hook(useEffect
、useMemo
、useCallback
など)は、Fast Refresh 中常に更新されます。Fast Refresh が発生している間、依存関係のリストは無視されます。
例えば、useMemo(() => x * 2, [x])
を useMemo(() => x * 10, [x])
に編集すると、x
(依存関係)が変更されていなくても再実行されます。React がこれをしなければ、編集が画面に反映されないでしょう!
時には、これが予期せぬ結果を引き起こすことがあります。例えば、空の依存関係配列を持つ useEffect
でさえ、Fast Refresh 中に一度再実行されます。
しかし、useEffect
が時折再実行されることに耐えられるコードを書くことは、Fast Refresh がなくても良い習慣です。後で新しい依存関係を導入しやすくなり、React Strict Mode によって強制されるため、有効化を強く推奨します。