stateとは
React コンポーネントで管理される状態です。
state
が更新されると、コンポーネントは再レンダリングされます。
状態を持つ必要があるコンポーネントでstate
を利用します。
- カウンター
- タイマー など
useState
state
を利用する場合、 useState
メソッドを利用します。
useState
の戻り値として、状態と状態更新するための更新関数が返されます。
詳しくみていきましょう。
const [state, setState] = useState(initialState);
-
initialState
はstate
の初期値を指定 -
useState
は二つの要素を持つタプルを返す。-
state
=> state の値 -
setState
=> state を更新するための関数
-
この setState
関数を使って、新しい state 値を設定することができます。
更新が発生すると、コンポーネントはその state をもって再レンダリングされます。
型指定
state
変数の型は初期値を元に推論されます。
そのため、state
の初期値がnull
の場合などは、明示的に型を指定します。
const [state, setState] = useState<number | null>(null);
Reactの単方向データフロー / Stateのリフトアップ
Reactは 単方向データフロー を採用しています。
データ(state
, props
)が親コンポーネントから子コンポーネントへ一方的に流れるという設計原則です。
そのため、複数コンポーネントで共通して管理する必要があるstate
は 親コンポーネントで管理され、 更新関数が子コンポーネントに渡されることが多いです。
これを stateのリフトアップ と言います。
画像引用
https://www.techpit.jp/courses/22/curriculums/23/sections/197/parts/696
サンプルコード
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';
const ParentComponent = () => {
const [count, setCount] = useState(0);
const incrementCount = () => {
setCount(count + 1);
};
return (
<div>
<h1>Count: {count}</h1>
<ChildComponent onIncrement={incrementCount} />
</div>
);
};
export default ParentComponent;
import React from 'react';
const ChildComponent = ({ onIncrement }) => {
return (
<button onClick={onIncrement}>Increment</button>
);
};
export default ChildComponent;
setStateにコールバックを渡す
stateの更新は非同期に行われます。
そのため、以下のようなコードは連続してクリックされた場合、stateの値が想定された値にならない可能性があります。
const handleClick = () => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
};
対策として、更新用のコールバック関数を渡すことができます。
この関数は実行時の最新のstate
値を引数に取るため、非同期で処理された場合も問題なくカウントアップが実行されます。
const handleClick = () => {
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
};
Hooksの呼び出しは関数コンポーネントのTOPで行う必要がある
React では、Hooks の呼び出し順がコンポーネントの各レンダリング間で一貫性を保つため、Hooks を関数コンポーネントのトップレベルでのみ呼び出す必要があります。
import { useState } from 'react';
const SampleComponent = () => {
if(true){
// NG: `React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.`
const [count, setCount] = useState(0);
}
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount((c) => c + 1)}>click</button>
</div>
);
};
export default SampleComponent;
オブジェクト / 配列 のstate管理
setState
を使用して state を更新する際は、特にオブジェクトや配列の場合、新しい参照を作成する必要があります。
参照が変更されていない場合、コンポーネントは再レンダリングされません
以下のように、既存の配列、オブジェクトを複製する必要があります。
const newArray = [...array];
const newState = {...state, updatedProperty: newValue};
まとめ
以上です。
本記事では表面的なところしか触れていませんが、メカニズムの理解がすごく楽しめました。