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 tree外からインポートされたファイルを編集すると、Fast Refreshは全体のリロードに頼ることになります。たとえば、Reactコンポーネントをレンダリングするファイルがありつつ、非Reactコンポーネントからインポートされている値をエクスポートする場合があります。例えば、コンポーネントが定数をエクスポートし、非Reactユーティリティファイルがそれをインポートしている場合です。この場合、定数を別ファイルに移行し、両方のファイルにインポートすることを検討してください。これにより、Fast Refreshが再度有効になります。他の場合も通常、同様の方法で解決できます。
エラーの耐性
構文エラー
開発中に構文エラーが発生した場合、修正して再度ファイルを保存できます。エラーは自動的に消え、アプリをリロードする必要はありません。コンポーネントの状態は失われません。
ランタイムエラー
コンポーネント内でランタイムエラーを引き起こすミスを犯した場合、コンテキストオーバーレイ画面が表示されます。エラーを修正すると、オーバーレイが自動的に閉じられ、アプリはリロードされません。
エラーがレンダリング中に発生しなかった場合、コンポーネントの状態は保持されます。もしエラーがレンダリング中に発生した場合、Reactは更新されたコードを使用してアプリケーションを再マウントします。
アプリにerror boundariesがある場合(本番環境での優雅な失敗には良いアイデアです)、レンダリングエラー後の次回の編集でレンダリングを再試行します。これにより、エラーboundaryがあると、常にrootアプリ状態にリセットされることを防ぐことができます。しかし、エラーboundaryはあまりに細かくしないように設計する必要があります。これらは本番環境では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;
を挿入できます - インポートは大文字小文字を区別することに注意してください。不完全なインポートは、実際のファイル名と一致しない場合に、fastおよびfullリフレッシュを失敗させる可能性があります
たとえば、
'./header'
と'./Header'
Fast RefreshとHooks
可能であれば、Fast Refreshは編集間でコンポーネントの状態を保持しようとします。特に、useState
とuseRef
は、それらの引数やHook呼び出しの順序を変更しない限り、以前の値を保持します。
依存関係を持つHooks、例えばuseEffect
、useMemo
、useCallback
はFast Refresh中に常に更新されます。Fast Refreshが行われている間は、依存関係のリストは無視されます。
たとえば、useMemo(() => x * 2, [x])
をuseMemo(() => x * 10, [x])
に編集すると、依存関係x
が変更されていなくても再実行されます。それをしないと、編集内容が画面に反映されないことになります!
時折、予期しない結果を招くことがあります。たとえ、空の依存関係の配列を持ったuseEffect
であっても、Fast Refresh中に一度だけ再実行されます。
しかし、useEffect
が時折再実行されることへの耐性を持ったコードを書くことは、たとえFast Refreshがなくても良い実践です。それは、後で新しい依存関係を追加しやすくし、React Strict Modeによっても強制されます。これを有効にすることを強くおすすめします。