はじめに
Reactの学習のため、Reactの実装には欠かせないReact Hooksについてまとめてみようと思います!React Hooksとは
関数コンポーネント内で状態やライフサイクルなどのReactの機能を利用するためのAPIです。 React Hooksを利用することでコンポーネントの再利用性を高め、ロジックの隔離と再利用を容易にするなどのメリットがあります。 今回はReact Hooksの中でも使用頻度が高い以下の3つの内容をまとめてみます。- useState
- useEffect
- useRef
useState
説明
関数コンポーネント内で状態管理を行うためのHookです。このHookを使用することで関数コンポーネントでも状態を持つことができ、その状態はコンポーネントの再レンダリングを通じて保持されます。構文
const [state, setState] = useState(initialState);
initialState:状態の初期値。コンポーネントが最初にマウントされた時にのみ使用されます。
state:現在の状態の値。
setState:状態を更新する関数。この関数に新しい状態の値を渡すことでコンポーネントが再レンダリングされ、新しい状態が反映されます。
注意点
- 状態を更新する場合は非同期に行われます。そのため状態の更新が即時に反映されない場合があり、複数回同時に値を更新する際は注意が必要
- 複数の状態更新が一度に行われる場合、更新をバッチ処理することがあります。そのためコンポーネントの再レンダリングは1回のみ発生する可能性があります
- 値を更新する場合、使用しているコンポーネントは再レンダリングされます。子コンポーネントも再レンダリングの対象になります
useEffect
説明
副作用フック(side effects)とも呼ばれ、コンポーネントの状態が変化したタイミングで実行すべき処理を定義することができます。クラスコンポーネントにおける
- componentDidMount(componentが描画された後)
- componentDidUpdate(componentが更新された後)
- componentWillUnmount(componentがDOMから削除される時)
のライフサイクルメソッドに相当します。
そのためuseEffectは主に以下の用途で使用されることが多いです。
- コンポーネントのマウント/アンマウント時に何かを行いたい場合(例:データのフェッチやイベントリスナーの設定/解除)
- コンポーネントの状態やpropsが更新された時に何かを行いたい場合
構文
useEffect(() => {
// 副作用
// クリーンアップ関数(必須ではない)
return () => {
// コンポーネントのアンマウント時に実行されるコード
};
}, [依存配列]);
useEffectの第二引数には依存配列を渡すことができますが、
渡す配列によって動作が異なります。
- 依存配列を省略: コンポーネントがレンダーされる毎に副作用が実行される
- 空配列: マウント時に1回だけ副作用が実行され、アンマウント時にクリーンアップ関数があれば実行される
- 値を含む依存配列: 指定された依存配列内のいずれかの値が変更された場合にのみ副作用が実行される
またクリーンアップ関数に関してはコンポーネントのアンマウント時や依存配列内の値が変更される前に実行されます。
注意点
- useEffectを多用するとコードの可読性が低くなり、発火のタイミングがわかりづらくなる(まずはuseEffect以外で実装できないか考えてみることが大事)
- 依存配列に不必要な値を含めると副作用が必要以上に頻繁に実行され、パフォーマンスの低下を引き起こす可能性がある
- 複数のuseEffectがある場合、宣言された順番に実行される。そのため複数のuseEffectで依存関係が重複している場合、副作用間で意図しない競合を避けるようにする(そもそも依存関係が重複しないように設計、管理するのが好ましい)
useRef
説明
Reactのrefを関数コンポーネントで使用するためのHookです。 DOM要素への参照を保持し、不必要な再レンダリングを避けたい時に利用されることが多いです。構文
useRef(initial)
initial:初期値
DOM要素への参照の保持
useRefを使用してDOM要素にアクセスすることができます。useRefで生成されたrefオブジェクトを要素のref属性に割り当てることで、その要素への参照を保持できます。function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>フォーカス</button>
</>
);
}
上記はinputタグの参照を保持している例です。
ボタンをクリックすることでonButtonClickが処理されますが、その関数内で保持しているinputEl(inputタグ)にフォーカスを当てるような処理をしています。
参照する場合はcurrentプロパティを通じてアクセスします。
再レンダリングさせずに情報を更新する
useRefはcurrentプロパティに何らかの値を保持しますが、currentプロパティの更新はReactのレンダリングサイクル外で行われます。つまり、useRefのcurrentプロパティを更新しても、Reactはそのコンポーネントを再レンダリングしません。function Timer() {
const countRef = useRef(0);
// マウント時に1度副作用が実行される
useEffect(() => {
countRef.current = countRef.current + 1;
console.log(`コンポーネントが${countRef.current}回更新されました。`);
},[]);
// countRef.currentの更新があっても、このコンポーネントは再レンダリングされない
return <div>TEST</div>;
}
上記のコードではマウント時にuseEffectを通じてcountRefの値を更新していますが、値の更新の際にコンポーネントは再レンダリングされません。
再レンダリングされない理由としては、useRefで作成されたオブジェクトのcurrentプロパティはReactの状態管理システムの外にあります。そのためcurrentプロパティを更新してもReactの状態管理システムは何も検知せず、結果として再レンダリングのトリガーとはならないため再レンダリングされないようです。
注意点
- useRefで保持されるオブジェクトのcurrentプロパティはどのライフサイクルでも変更可能であり、参照先の変更はReactのレンダリングシステムの外で発生します。そのため不用意に変更することは予期しない動作や不具合を引き起こす可能性があります
最後に
今回はReact hooksの中でも基本的な3つをまとめてみました。
他のhookも時間があればまとめてみようと思います!