高速更新

高速更新は、 React コンポーネントに加えられた変更の即フィードバックを提供する Next.js の機能です。高速更新は 9.4 もしくはそれ以上の全ての Next.js アプリケーションでデフォルト有効になっています。 Next.js の高速更新が有効になったことで、ほとんどの変更はコンポーネントの状態を失うことなく瞬時に表示されます。

どのように機能するか

  • React コンポーネントを export するだけのファイルを変更すると、高速更新はそのファイルのコードのみを更新し、コンポーネントを再レンダリングします。スタイル、レンダリングロジック、イベントハンドラ、副作用などを含むファイルの全てを変更できます。
  • React コンポーネントではないエクスポートを含むファイルを変更する場合、高速更新はそのファイルとインポートしている他のファイルを両方再実行します。もし Button.jsModal.jstheme.js をインポートしている場合、 theme.js の変更は両方のコンポーネントを更新します。
  • 最後に React ツリーの外にあるファイルによってインポートされているファイルを変更すると、高速更新はフルリロードの実行にフォールバックします。 React コンポーネントをレンダリングするだけではなく、 React ではないコンポーネントによってインポートされる値をエクスポートするファイルがあるでしょう。例えば、コンポーネントが constant をエクスポートし React ではないユーティリティファイルがそれをインポートする場合があります。その場合、 constant を別ファイルに移植し両方から読み込むことを検討してください。そうすれば、高速更新が再び有効になります。他の場合も大抵同じ方法で解決できます。

エラー回復力

シンタクスエラー

開発中にシンタックスエラーを作った場合、それを再度修正して保存できます。エラーは自動的に消え、アプリケーションをリロードする必要はありません。コンポーネントの状態を失いません。

ランタイムエラー

コンポーネントの中でランタイムエラーに繋がるミスを作った場合、コンテキストオーバーレイが表示されます。エラーを修正することで、アプリケーションをリロードすることなく、自動的にオーバーレイは閉じられます。

レンダリングの際にエラーが発生しなかった場合、コンポーネントの状態は保持されます。レンダリングの際にエラーが発生した場合、 React は変更されたコードを使用してアプリケーションを再マウントします。

アプリに error boundary がある場合(これは本番環境で安全に失敗するのに適しています)、それらはレンダリングエラーの後、次の変更で再レンダリングします。つまり、error boundary があることによって、常にルートアプリの状態がリセットされることを防ぐことができます。しかし、error boundary は細かくしすぎないように注意してください。これらは React によって本番環境で使用され、常に意図的に設計されるべきです。

制限

高速更新は変更しているコンポーネントのローカル React 状態を保持しようとしますが、それが安全である場合に限ります。ファイルの各変更でローカル状態がリセットされる理由は次のとおりです:

  • クラスコンポーネントのローカル状態は保持されません(関数コンポーネントと Hooks のみが状態を保持します。)
  • 変更しているファイルには、 React コンポーネントに加えて他のエクスポートも含まれる可能性があります。
  • 場合によって、ファイルが HOC(WrappedComponent) などの高階コンポーネントを呼び出した結果をエクスポートします。返されたコンポーネントがクラスの場合、その状態はリセットされます。
  • export default () => <div />; のような匿名アロー関数は、高速更新でローカルコンポーネントの状態が保持されないようにします。大規模なコードベースの場合は、 name-default-component codemod を使用できます。

より多くのコードベースが関数コンポーネントと Hooks に移動するにつれて、より多くの場合に状態が保持されることを期待できます。

アドバイス

  • 高速更新はデフォルトで関数コンポーネント(と Hooks)の React ローカル状態を保持します。
  • 場合によって、強制的に状態をリセットしてコンポーネントを再マウントしたいこともあるでしょう。例えば、マウント時にのみ発生するアニメーションを微調整する場合に便利です。これを行うには、変更しているファイルのどこかに // @refresh reset を追加します。このディレクティブはファイルに対してローカルであり、変更のたびにそのファイルで定義されているコンポーネントを再マウントするように高速更新に指示します。
  • 開発中に変更しているコンポーネントの中に、 console.logdebugger; を置くことができます。

高速更新と Hooks

可能であれば、高速更新は変更の間コンポーネントの状態を保持しようとします。特に、 useStateuseRef は 引数や Hook 呼び出しの順番を変更しない限り、以前の値を保持します。

useEffectuseMemouseCallback のような依存関係を持つ Hooks は高速更新の間、常に更新されます。高速更新が行われている間、依存関係のリストは無視されます。

例えば、 useMemo(() => x * 2, [x]) から useMemo(() => x * 10, [x]) へ変更しているとき、 x (依存関係)が変更されていなくても再実行されます。もし React がそれをしなかった場合、変更は画面に反映されていません!

場合によって、これは予期せぬ結果を導きます。例えば、依存関係の空の配列を持つ useEffect でも、高速更新の間 1 回再実行されます。

しかし、偶発的な useEffect の再実行に対して耐性のあるコードを書くことは、高速更新がたとえ無くても良い習慣です。後で新しい依存関係を簡単に導入できるようになり、私達が有効にすることを強く推奨している React Strict Mode によって適用されます。