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