ホットモジュールリプレースメント
ホットモジュールリプレースメント(HMR)は、ページをリロードせずにアプリ内のモジュールを更新する技術です。 これは素晴らしい開発体験であり、React RouterはViteを使用している場合にこれをサポートします。
HMRは、更新間でブラウザの状態を維持するために最善を尽くします。 たとえば、モーダル内にフォームがあり、すべてのフィールドに入力したとしましょう。 コードに変更を保存するとすぐに、従来のライブリロードではページがハードリフレッシュされ、すべてのフィールドがリセットされます。 変更を加えるたびに、モーダルを_再度_開き、フォームを_再度_入力する必要があります。
しかし、HMRを使用すると、すべての状態が_更新間で_保持されます。
React Fast Refresh
Reactには、ボタンをクリックするなどのユーザーインタラクションに応じて仮想DOMを介してDOMを更新するメカニズムがすでにあります。 Reactがコードの変更に応じてDOMの更新も処理できたら素晴らしいと思いませんか?
それがまさにReact Fast Refreshの目的です! もちろん、Reactは一般的なJavaScriptコードではなくコンポーネントに関するものなので、React Fast RefreshはエクスポートされたReactコンポーネントのホットアップデートのみを処理します。
ただし、React Fast Refreshには注意すべきいくつかの制限があります。
クラスコンポーネントの状態
React Fast Refreshは、クラスコンポーネントの状態を保持しません。 これには、内部的にクラスを返す高階コンポーネントが含まれます。
export class ComponentA extends Component {} // ❌
export const ComponentB = HOC(ComponentC); // ❌ HOCがクラスコンポーネントを返す場合は機能しません
export function ComponentD() {} // ✅
export const ComponentE = () => {}; // ✅
export default function ComponentF() {} // ✅名前付き関数コンポーネント
関数コンポーネントは、React Fast Refreshが変更を追跡するために、匿名ではなく名前付きである必要があります。
export default () => {}; // ❌
export default function () {} // ❌
const ComponentA = () => {};
export default ComponentA; // ✅
export default function ComponentB() {} // ✅サポートされているエクスポート
React Fast Refreshは、コンポーネントのエクスポートのみを処理できます。React Routerは、 action、headers、links、loader、metaなどのルートエクスポートを管理しますが、ユーザー定義のエクスポートは完全なリロードを引き起こします。
// これらのエクスポートは、React Router Viteプラグインによって処理されます
// HMR互換にするため
export const meta = { title: "Home" }; // ✅
export const links = [
{ rel: "stylesheet", href: "style.css" },
]; // ✅
// これらのエクスポートは、React Router Viteプラグインによって削除されます
// そのため、HMRに影響を与えることはありません
export const headers = { "Cache-Control": "max-age=3600" }; // ✅
export const loader = async () => {}; // ✅
export const action = async () => {}; // ✅
// これはルートモジュールのエクスポートでもコンポーネントのエクスポートでもないため、
// このルートの完全なリロードを引き起こします
export const myValue = "some value"; // ❌
export default function Route() {} // ✅👆 ルートは、とにかくそのようなランダムな値をエクスポートするべきではありません。 ルート間で値を再利用したい場合は、ルート以外の独自のモジュールにそれらを貼り付けてください。
export const myValue = "some value";フックの変更
React Fast Refreshは、フックがコンポーネントに追加または削除されている場合、コンポーネントの変更を追跡できません。これにより、次のレンダリングのためだけに完全なリロードが発生します。フックが更新された後、変更は再びホットアップデートになるはずです。たとえば、コンポーネントにuseStateを追加すると、次のレンダリングでそのコンポーネントのローカル状態が失われる可能性があります。
さらに、フックの戻り値を分割代入している場合、分割代入されたキーが削除または名前変更された場合、React Fast Refreshはコンポーネントの状態を保持できません。 例:
export default function Component({ loaderData }) {
const { pet } = useMyCustomHook();
return (
<div>
<input />
<p>私の犬の名前は{pet.name}です!</p>
</div>
);
}キーpetをdogに変更した場合:
export default function Component() {
- const { pet } = useMyCustomHook();
+ const { dog } = useMyCustomHook();
return (
<div>
<input />
- <p>私の犬の名前は{pet.name}です!</p>
+ <p>私の犬の名前は{dog.name}です!</p>
</div>
);
}React Fast Refreshは、状態<input />❌を保持できません。
コンポーネントキー
場合によっては、Reactは変更されている既存のコンポーネントと追加されている新しいコンポーネントを区別できません。Reactは、これらのケースを曖昧さを解消し、兄弟要素が変更されたときに変更を追跡するためにkeyが必要です。