概要
Stateはコンポーネントがユーザ入力のような情報を「記録」することを可能にします。 例えば、フォームコンポーネントがは入力値を保存するためにStateを使用したりします。
function FormComponent() {
const [text, setText] = useState("");
// ...
コンポーネントにStateを追加するには以下のどちらかのHooksを使用します。
useState
useStateはコンポーネントのトップレベルで宣言され、[<current state>, <set function>]
のような配列で命名されます。useState
の引数にはそのstateの初期値が与えられます。
const [age, setAge] = useState(42);
set関数で新たにstateが更新されても、その実行関数内では更新される前の値になることに注意です。
更新される値を使用するには再レンダリング後でないといけません。
function handleClick() {
setCount(count + 1);
console.log(count) // ← initial Stateが0の場合 0が出力される
}
useStateを使った例
useReducer
useReducerはコンポーネントのトップレベルで宣言され、reducerで状態を管理します。
useReducerは2つの項目の配列を返却します。1つ目は現在の状態を表すstate, 2つ目はstateを更新するためのdispatchメソッドです。
stateの更新をするにはアクションと呼ばれる、ユーザが行ったことを表すオブジェクトでdispatchメソッドを呼び出します。
function reducer(state, action) {
// ...
}
function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
function handleClick() {
dispatch({ type: 'incremented_age' });
}
// ...
useReducerはuseStateとよく似ていますが、イベントハンドラーからコンポーネントの外部にある単一の関数に状態更新のロジックを移動させることができます。
useReducerを使った例
useStateとUseReducerの使い分け
-
Code size (コードサイズ)
useStateを使用すると最初に書くコードが少なくなる
useReducerを使用すると reducer関数とdispatchアクションの両方を書かなければならないので、記述量は多くなるが同じようなstate更新がある場合は共通化ができる。 -
Readability (読みやすさ)
useStateは、状態の更新が単純であれば読みやすいが、複雑な更新になるとコンポーネントのコードが複雑になり可読性が低くなる場合がある。
useReducerは更新ロジックと何のイベントなのかのイベントハンドラーがキレイに分離できるため可読性が高い。 -
Debugging (デバッグ)
useStateで不具合が発生した場合、どこで間違った状態になったのかをデバッグするのが難しい。
useReducerでは、reducerにコンソールログ等を追加して、すべてのステートの更新とその理由(どのアクションによるものなのか)が確認できます。しかし、useStateより多くのコードを書くことになります。 -
Testing (テスト)
reducerは純粋な関数なので、コンポーネントに依存しないテストを書くことができます。つまりreducer単体でexportさせて単体テストを書くことができます。
コンポーネントが正しく更新されないことが頻繁に発生する場合にはreducerを使うことが推奨されています。
参考