コンポーネント間のpropsの受け渡し - React入門

連載目次 : React入門
前回の記事 : useRef / React Hooks

今回はReact Hooksで状態管理ができるuseReducerについて解説を行います。

useStateは単一のステートを管理するのに適していましたが、useReducerは複数のステートを管理するのに適したHooks APIとなります。

基本的な利用方法

useReducerを利用する場合にはReactのimport時にuseReducerを読み込む必要があります。

import React, { useReducer } from 'react';

初期ステートを作成しておき、

const initialState = {count: 0};

ステートを操作するためのreducer関数を作成します。

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1}
    case 'decrement':
      return {count: state.count - 1}
  }
}

そしてfunctionコンポーネントのトップレベルの位置でuseReducerを利用して状態を保持したstateオブジェクトと状態を変更するためのreducer関数を作成します。

const [state, dispatch] = useReducer(reducer, initialState)

こうすればstate.countとして現在のカウント数を増加させたり、dispatch({type: 'decrement'})という命令でカウントを減算させたり、dispatch({type: 'increment'})でカウントを加算することができます。

まとめると以下のように利用することができます。

import React, { useReducer } from 'react';

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
  }
}

export default function Foo() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

ただ、このような単純な状態管理はuseStateでも可能ですがuseReducerの利点は、ステートの更新ロジックをコンポーネントに非依存な外部のreducer関数に渡すことができる点です。

ロジックを分離

例えば以下のサンプルは入力されているテキストが0文字の場合もしくは15文字以上の場合はエラーを表示するというサンプルです。

const initialState = {
  text:'',
  error: 0
}

function reducer(state, action) {
  switch (action.type) {
    case 'input':
      // error = 0:none 1:empty 2:overflow
      const error = action.payload.length === 0 ? 1 : action.payload.length > 15 ? 2 : 0
      return {
        text: action.payload,
        error
      };
    case 'reset':
      return initialState;
  }
}

function Foo() {
  const [state, dispatch] = useReducer(reducer, initialState);
  
  const handleChange = useCallback(e=>{
    dispatch({type:'input',payload:e.target.value})
  },[])
  
  const handleReset = useCallback(()=>{
    dispatch({type:'reset'})
  },[])
   
  return (
    <>
      <input type="text" value={state.text} onChange={handleChange} />
      <input type="button" value="reset" onClick={handleReset} />
      {state.error === 1 && <p>空欄です</p>}
      {state.error === 2 && <p>15文字以上です</p>}
    </>
  );
}

ロジックがreducer関数に埋め込まれているので、コンポーネントがシンプルになり、testなどの記述が容易になります。

また、依存する変数が少なくなるので、useCallbackやuseMemoによるパフォーマンスチューニングもしやすくなります。

管理する状態が複雑になりそうな場合はuseStateではなくuserReducerの利用を検討してみるのも良いでしょう。

さらにuserReduceruseContextと組み合わせてコンポーネント間にまたがるステートを管理することができます。

次回はuseContextについて解説を行います。

連載目次 : React入門
前回の記事 : useRef / React Hooks

【告知】弊社のメンバーが執筆した書籍が発売されました!

弊社のメンバーが執筆した『初心者からちゃんとしたプロになる JavaScript基礎入門』が発売されました! JavaScriptの基礎からVue.jsまでをカバーしており、初学者から基本の復習をしたい方におすすめの1冊です。