コンポーネントやフックを呼び出すのは React

ユーザ体験を最適化するために必要に応じてコンポーネントやフックを呼び出すというのは React 自身の責務です。React は宣言型 (declarative) です。あなたは何 (what) をレンダーしたいのかだけを React に伝え、それをどうやって (how) ユーザにうまく表示するのかについては React が考えます。


コンポーネント関数を直接呼び出さない

コンポーネントは JSX 内でのみ使用すべきです。通常の関数として呼び出してはいけません。呼び出すのは React です。

レンダー中にコンポーネント関数をいつ呼び出すかを決定する必要があるのは React です。React では、これを JSX を使用して行います。

function BlogPost() {
return <Layout><Article /></Layout>; // ✅ Good: Only use components in JSX
}
function BlogPost() {
return <Layout>{Article()}</Layout>; // 🔴 Bad: Never call them directly
}

コンポーネントにフックが含まれている場合、ループや条件内でコンポーネントを直接呼び出すと、フックのルールにいとも簡単に違反してしまいます。

React にレンダーの指揮権を与えることで、多くの利点が得られます。

  • コンポーネントが単なる関数以上のものになる。ツリー内のコンポーネントの同一性に基づいた処理を行うフックを通じて、React はローカル state などの機能を追加できます。
  • コンポーネントの型情報を差分検出処理時に利用できる。React にコンポーネントの呼び出しを任せることで、ツリーの概念的構造について React はより多くの情報を得られます。たとえば <Feed> から <Profile> ページへとレンダーが移行するとき、React はそれらを再利用しようとせずに済みます。
  • React がユーザ体験を向上させられる。たとえば、大きなコンポーネントツリーの再レンダーがメインスレッドをブロックしないよう、複数のコンポーネントのレンダーの合間にブラウザに一部の作業を行わせることができます。
  • より良いデバッグ体験。ライブラリがコンポーネントのことを基本部品として認識していれば、開発中の調査のためのリッチな開発者ツールを構築できます。
  • より効率的な差分検出処理。React は、ツリー内のどのコンポーネントを再レンダーすべきか正確に把握し、残りをスキップします。これによりアプリの動作は高速でキビキビとしたものになります。

フックを通常の値として取り回さない

フックはコンポーネントまたはフックの内部でのみ呼び出すべきです。通常の値のように取り回してはいけません。

フックは、コンポーネントに React の機能を加えるために使用します。常に関数として呼び出すだけにし、通常の値のように取りまわしてはいけません。これによりローカル・リーズニングが可能に、すなわち開発者がそのコンポーネントだけを見てコンポーネントにできることをすべて理解できるようになります。

このルールを破ると、React はコンポーネントを自動的に最適化することができなくなります。

フックを動的に変更しない

フックは可能な限り「静的」であるべきです。つまり、動的に変更してはいけません。たとえば、高階 (higher-order) フックを書くべきではありません。

function ChatInput() {
const useDataWithLogging = withLogging(useData); // 🔴 Bad: don't write higher order Hooks
const data = useDataWithLogging();
}

フックはイミュータブルであるべきで、動的に変更するべきではありません。フックを動的に変更する代わりに、望ましい機能を持つ静的なバージョンのフックを作成してください。

function ChatInput() {
const data = useDataWithLogging(); // ✅ Good: Create a new version of the Hook
}

function useDataWithLogging() {
// ... Create a new version of the Hook and inline the logic here
}

フックを動的に使用しない

フックを動的に使用してはいけません。例えば以下のように、フックそのものを値としてコンポーネントに渡して依存性注入を行わないようにしてください。

function ChatInput() {
return <Button useData={useDataWithLogging} /> // 🔴 Bad: don't pass Hooks as props
}

代わりにコンポーネント内でインラインでフックをコールし、そこでロジックを処理するべきです。

function ChatInput() {
return <Button />
}

function Button() {
const data = useDataWithLogging(); // ✅ Good: Use the Hook directly
}

function useDataWithLogging() {
// If there's any conditional logic to change the Hook's behavior, it should be inlined into
// the Hook
}

こうすることで <Button /> を理解しデバッグするのがずっと簡単になります。フックを動的に使用すると、アプリの複雑さが大幅に増し、ローカル・リーズニングを妨げ、長期的にはチームの生産性を低下させます。また、条件付きでフックを呼び出すべきではないというフックのルールを誤って破る可能性も高まります。テストのためにコンポーネントをモックする必要がある場合は、代わりにサーバをモックして固定データで応答する方が良いでしょう。可能であれば、end-to-end テストでアプリをテストする方が通常はより効果的です。