【訳】React Hooksの5つの重要な原則

  • 2611単語
  • 13分
  • 27 Jul, 2024

現時点では、Reactはフロントエンドアプリケーションを作成するための最も人気のある選択肢の1つであることは間違いありません。これは欠点がないからではなく、Reactが長年にわたって巨大なコミュニティと大きな人気を築いてきたからです。

フックなしのReactを想像するのは難しいですが、残念ながら、開発者がそれらを過度に使用することがよくあります。その結果、依存配列、無駄な再レンダリング、およびそれらが引き起こすすべての混乱問題を解決しなければならなくなります。なぜなら、心の奥底で、Reactではフックを使用しなければ何も達成できないと恐れているからです。

この記事では、新しいReactプログラマーがコードを改善し簡素化するために知っておくべき5つの原則について説明します。

1. すべての関数がフックである必要はない

基本から始めましょう。定義を見てみましょう。

フックはJavaScript関数を使用して定義されますが、特殊な再利用可能なUIロジックを表し、呼び出し位置に制限があります。フックを使用する場合、フックのルールに従う必要があります。

フックは関数のように見えますが、いくつかの違いがあります。

  • フックは関数コンポーネントまたはカスタムフック内でのみ使用できます。
  • フックの名前は常に「use」で始まり、その後に大文字が続きます。
  • カスタムフック内に他のフックの呼び出しが含まれていない場合、それは関数でありフックではありません。これは重要です。なぜなら、これにより、状態ロジックや副作用が含まれているかどうかを判断するのに役立つからです。

これらの要件を満たすために、useBooleanという名前のシンプルなカスタムフックを作成しましょう。これを使用してパネル、ダイアログ、要素の表示/非表示を切り替えることができます。

公式ドキュメントを見ると、フックが返す関数をuseCallbackでラップすることをお勧めしています。私たちもそうしましょう。

1
interface ICallbacks {
2
setFalse: () => void;
3
setTrue: () => void;
4
toggle: () => void;
5
}
6
7
const useBoolean = (initialValue: boolean): [boolean, ICallbacks] => {
8
const [value, setValue] = useState(initialValue);
9
10
const setFalse = useCallback(() => {
11
setValue(false);
12
}, []);
13
14
const setTrue = useCallback(() => {
15
setValue(true);
16
}, []);
17
18
const toggle = useCallback(() => {
19
setValue((curValue) => !curValue);
20
}, []);
21
22
return [value, { setFalse, setTrue, toggle }];
23
};

基本を覚えたら、いくつかの微妙な違いを深く掘り下げることができます。

2. 再レンダリングを理解する

なぜなのか疑問に思うかもしれません。

Reactがどのように機能するか、setter関数を介してコンポーネントの状態を変更したときに何が起こるかを理解することが重要です。一見すると、状態を変更し、その結果がすぐに表示されるように見えますが、実際にはどうでしょうか?

状態を変更すると何が起こるかを知っていると、useEffectや依存配列を持つ他のフックがトリガーされる理由とタイミングを理解しやすくなります。

簡単な例を見てみましょう。最初にボタンを押してonChangeTextを呼び出し、「newValue」という値を渡すと想像してください。

1
const [text, setText] = useState("defaultValue");
2
3
const onChangeText = (value: string) => {
4
// value は "newValue"
5
setText(value);
6
7
console.log(text); // ここでの値は何でしょうか?
8
};

コンソールに「newValue」が表示されると思われるかもしれませんが、実際には「defaultValue」が表示されます。なぜでしょうか?新しい値は再レンダリング後にのみ利用可能だからです。

発生するステップを理解することが重要です:

  1. setter関数を介して状態を変更し、Reactにアクションを取るよう指示します。
  2. レンダリング。Reactはコンポーネントを呼び出して、新しいJSXを計算し、それが返されます。
  3. 提出。変更の計算が完了すると、ReactはDOMを変更します。最小限のアクションが適用されます。
  4. 前のステップの後、画面上に視覚的な変更が表示されます(「ブラウザレンダリング」)。

状態内の値を変更するたびに、これらのステップが毎回完了することを覚えておく必要があります。

3. useStateが常に正解ではない

Reactでは、コンポーネントの状態を管理する方法が2つあります——useStateとuseReducer。2つ目の方法は、より複雑なオブジェクトの状態に適しているため、あまり人気がありません。正直なところ、新しいプログラマーには一見すると複雑すぎるように見えるかもしれませんが、実際にはそうではありません。

しかし、useStateは非常にシンプルで理解しやすいように見えるため、新しいプログラマーは実際に必要以上にそれを使用することがよくあります。

useStateは、ユーザーの操作に応じてコンポーネントの状態を管理するために設計されています。レンダリングを行わずにいくつかの内容を記憶したい場合、それを状態に含めるべきではないかもしれません。useRefがより適切な選択です。

次の場合:

  • 再レンダリング中に値を記憶し、ユーザーに表示しない場合。
  • 状態内にすでにデータがあるか、propsを介して受け取っているが、それを変換する必要がある場合;新しいuseStateオブジェクトに新しい値を保存する必要はありません。新しい変数を作成し、それを操作して無駄な再レンダリングを引き起こさないようにします。

値を状態に保存する必要がある場合:

  • 値が変更されたときにコンポーネントを再描画したい場合;最も一般的な例は、パネルの表示/非表示、スピナー、エラーメッセージ、および配列の変更です。

コードを次のように簡素化します:

1
/**
2
次のコードは無駄な再レンダリングと不必要なuseEffectの使用を引き起こします。
3
nameやdescriptionが変更されると、Reactはコンポーネントを再レンダリングします
4
**/
5
6
const [name, setName] = useState("name");
7
const { description, index } = props;
8
const [fullName, setFullName] = useState("");
9
10
useEffect(() => {
11
setFullName(`${name} - ${description}`);
12
}, [name, description]);

次のように変更します:

1
/**
2
Reactのデフォルトの動作を利用して、
3
nameやdescriptionが変更されたときに正しい値を取得し、
4
再レンダリングを一度もトリガーしません。
5
**/
6
const [name, setName] = useState();
7
const { description, index } = props;
8
const nameWithDescription = `${name} - ${description}`;

4. useEffectの使用には注意が必要

useEffectは、コンポーネントと外部システムを同期するためのReactフックです。

公式ドキュメントを見てみましょう

しかし実際には、useEffectの使用頻度は実際に必要な頻度をはるかに上回っています。コンポーネントのマウント時にデータを取得するのには非常に適していますが、残念ながら新しいプログラマーはこのフックを使用して状態を変更する傾向があります。これは最良の解決策ではありません。

コンポーネント内で次々とuseEffectを書いていることに気づいたら、一度立ち止まってコードを見直してください。通常、それらは必要ありませんし、それらをすぐに取り除くことができます。

次の条件に該当する場合、useEffectは必要ありません:

  • ユーザーのイベント(クリック)を処理する必要がある場合;どのアクションが特定のロジックをトリガーするかを知っている場合、useEffectを使用してそのロジックを呼び出す必要はありません。
  • 状態やpropsからデータを変換してレンダリングする必要がある場合。依存配列に反応する値やロジックを含めると、useEffectが頻繁に呼び出され、無限ループが発生する可能性があります。

次の条件に該当する場合、useEffectを選択する必要があります:

  • コンポーネントのマウント時にデータを取得したい、間隔を設定したい、または他のシステムと状態を同期させたい場合

5. useRefを恐れない

残念ながら、useRefは過小評価されています。最も人気のあるフックの1つではありませんが、非常に役立ちます。使用方法と使用場所を知っていると、非常に効果的です。

基本から始めましょう。

useRefは、再レンダリングが不要な値を参照するためのReactフックです。——React公式ドキュメントより。

DOM内のノードを参照するJavaScriptオブジェクトを作成する場合でも、単純な値を参照する場合でも、ReactはuseRefを介して作成した値を記憶し、再レンダリング中に失われることはありません。

useRefは私たちに何をもたらしますか?

  • DOM内の要素に簡単にアクセスできます。たとえば、入力フィールドの値を取得したり、特定の要素にフォーカスしたり、高さや幅を取得したり、画面の特定の部分にスクロールしたりすることができます。
  • 再レンダリングせずに必要なデータを記憶することができます。たとえば、カウンターやタイマーが必要な場合は、useStateではなくuseRefを選択します。

例:

1
// 数値を参照
2
const refCount= useRef(0);
3
// 入力要素を参照
4
const refInputField = useRef(null);
5
6
/**
7
値にアクセスするには、currentプロパティを使用する必要があります。
8
useRefは再レンダリングをトリガーしないため、依存配列を気にせずに
9
useEffect内で使用できます
10
*/
11
12
const onClick = () => {
13
refCount.current = refCount.current + 1;
14
refInputField.current.focus();
15
}
16
17
return (
18
<>
19
<button onClick={onClick}>
20
Click me!
21
</button>
22
<input ref={refInputField}></input>
23
</>
24
);