useReducerの実装方法まとめ
こんにちは、CURUCURU エンジニアの水谷です。 今月からCURUCURUエンジニアで勉強会をすることになりました。
各自でその月に悩んだこと、学習したことなどをブログでまとめつつ、それをエンジニア間で共有するという勉強会です。
今回はその勉強会でも共有する記事を書いていきます。
useReducerの実装方法まとめ
早速ですが、今回はuseReducer
を使った実装方法をまとめていきます。
useReducer
はReact hookというのはわかるuseState
はなんとなく理解できた- 構造が複雑そうでよくわからない
こういった状態のときにuseReducer
を触る機会があったのですが、ようやく理解できてきたので、本記事でまとめておこうと思います。
なお、本記事の筆者はReact歴2ヶ月なので、初心者の人にも参考になると思います。
useReducerの概要
そもそもuseReducer
にどういったメリットがあるかについてですが、useState
の管理では複雑になってくるときに役に立ちます。
useState
は状態を管理するときに使いますが、複数要素の状態を管理したり、状態によって処理を分けたいときなどは、useState
で書くと少々複雑な書き方になるときがあります。
それをuseReducer
で解決していくという形になります。
基本的にはuseState
で管理しつつ、複雑になりそうなときにuseReducer
を使うという流れで今は理解しています。
useReducerで必要なもの
useReducer
で検索すると色々な書き方が出てきます。これも僕が当初混乱した理由の1つでした。
だいたいは同じ意味になるのですが、例えばuseContext
と併せた内容が書いてあったり、突然Redux
の話が出てきたりする記事があったりして混乱したので、必要なものをまとめておきます。
- State
- ActionType
- Reducer
基本的に必要なものとして上記を覚えておくと良いです。CURUCURUではそれぞれ別のファイルにして管理しています。順番に書いていきます。
State
まずState
についてですが、これはその名の通りで、コンポーネントの状態を保持しているだけになります。
state.ts
export interface HogeState = {
count: number;
};
export const initialState: HogeState = {
count: 0;
}
初期値も併せて定義しておきます。 このコードはファイルを分けているの想定なのでexportもつけています。
ActionType
次にActionType
についてです。
ActionType
はいわゆる型です。どんな処理かの宣言(type
)と計算ごとの引数の型(action
)を定めています。
action_types.ts
export const HogeInc = Symbol('HOGE_INC');
export const HogeDec = Symbol('HOGE_DEC');
export type HogeActions = {
type: typeof HogeInc;
args: { hogeinc: number };
} | {
type: typeof HogeDec;
args: { hogedec: number }
};
Symbol
で宣言することで同じ値が存在しないようになります。
Reducer
そして、Reducer
です。
ActionType
で割り振られた処理をしつつ、State
を返す処理をします。
reducers.ts
import{ HogeInc, HogeDec } from './action_types';
import type { HogeActions } from './action_types';
import type { HogeState } from './state';
export const reducer: React.Reducer<HogeState, HogeActions> = (state, action) => {
switch(action.type) {
case HogeInc:
return { count: state.count + action.args.hogeinc };
case HogeDec:
return { count: state.count - action.args.hogedec };
}
}
このようにすると、action.type
の値によって処理を分けることが出来るので、返すState
の値を変えることができます。
useReducerを使った処理
では、useReducer
を使うと、どういった処理になるのかを見ていきます。
import React, { useReducer } from 'react';
import * as HogeActionTypes from './action_types';
import { reducer } from './reducers'
import { initialState } from './state'
export const HogeCounter = () => {
const [state, dispatch] = React.useReducer(reducer, initialState);
const onIncrement = () => {
dispatch({
type: HogeActionTypes.HogeInc,
args: { hogeinc: 1 }
});
};
const onDecrement = () => {
dispatch({
type: HogeActionTypes.HogeDec,
args: { hogedec: 1 }
})
};
return (
<div className="_hogeBox">
<div className="_hogeInner">
<input type="button" value="inc" onClick={onIncrement} />
<input type="button" value="dec" onClick={ondecrement} />
<div>{state.count}</div>
</div>
</div>
);
};
こういった形で処理を書けます。
ポイントはdispatch
の部分で、ボタンをクリックしたときに、インクリメントかデクリメントか処理を割り振って、状態を管理することが出来る点です。
dispatch
の部分で引数をActionType
に渡す。- 渡した引数に応じて
reducer
が処理をする。
つまり、この関数コンポーネントの中では、actiontype
のtype
とargsを渡すことで、そこから先はreducer
が処理をしてくれます。
これによって、関数コンポーネントの中はきれいにコードが書くことが出来る点もuseReducer
の魅力かなと思います。
今回はcount
というState
が1つだけなので、useState
で良いかもしれないですが、他にState
が増えてきたりして複雑になってきたら威力を発揮すると思います。
最後に
というわけで、今回はuseReducer
に関してまとめてみました。
当初全く分からなかった状態から見れば、まあわりと理解できてきたかなと思います。良い先輩たちのおかげですね。
引き続きスキルアップしていきたいです。 以上、CURUCURUエンジニアの水谷でした。
メンバー募集
CURUCURUでは開発メンバーを募集中です。 CURUCURUの開発に興味があったり、モダンな開発環境で挑戦してみたいという方がいましたら、ぜひこちらも覗いてみてください!