useReducerを使ってデータ一覧を表示する
はじめに
useReducerを使ってデータ一覧を表示する実装をしてみました。
useReducerとは、状態管理に使うことができるReact Hooksの一つ。
状態に応じた処理を管理できます。
公式ドキュメント:
開発環境
- react: 18.2.0
完成イメージ
データ一覧を取得して表示
完成イメージのソースコードはこちらです。
import { REQUEST_STATE } from "./GoalsIndexContext";
// 初期状態を定義
export const initialState = {
fetchState: REQUEST_STATE.INITIAL,
goalsList: [],
};
// リクエストのアクション状態を定義
export const goalsActionTypes = {
FETCHING: "FETCHING",
FETCH_SUCCESS: "FETCH_SUCCESS",
};
// Reducerを定義
export const GoalsIndexReducer = (state, action) => {
switch (action.type) {
case goalsActionTypes.FETCHING:
return {
...state,
fetchState: REQUEST_STATE.LOADING,
};
case goalsActionTypes.FETCH_SUCCESS:
return {
fetchState: REQUEST_STATE.OK,
goalsList: action.payload.goals,
};
default:
throw new Error();
}
};
import React, { useEffect, useReducer } from "react";
// apis
import { displayGoals } from "../apis/goals";
import { Box, Card, Flex } from "@chakra-ui/react";
// reducers
import {
initialState,
goalsActionTypes,
GoalsIndexReducer,
} from "../state/GoalsIndexReducer";
import TodaysGoalsList from "./organisms/lists/TodaysGoalsList";
export const Goals = () => {
const [state, dispatch] = useReducer(GoalsIndexReducer, initialState);
useEffect(() => {
dispatch({ type: goalsActionTypes.FETCHING });
displayGoals().then((data) =>
dispatch({
type: goalsActionTypes.FETCH_SUCCESS,
payload: { goals: data.goals },
})
);
}, []);
return (
<Flex align="center" justify="center" direction="column">
<Box bg="blue" margin="0" w="4xl">
<Card margin="30px" padding="40px" bg="green">
{state.goalsList.map((goal) => (
<TodaysGoalsList
key={goal.id}
goal={goal.goal}
reward_memo={goal.reward_memo}
reward_icon={goal.reward_icon_id_id}
achievement_icon={goal.achievement_icon_id_id}
timestamp={goal.created_at}
/>
))}
</Card>
</Box>
</Flex>
);
};
export default Goals;
状態管理のイメージ
ユーザーがインプットする(URLアクセス)
↓
アクション(一覧取得)
↓
dispatchでReducerに状態(state)を通知
↓
アクションと状態に応じて、Reducerが処理を実行する
※useReducerを使うメリット
・状態管理のロジックを共通化できるので、複数コンポーネントで使いたいときに便利
・useStateと違う点:複数のstateを一定のロジックにしたがってまとめて管理することができる
実装
■Reducerの作成
(1)Stateの初期状態を定義します
// 初期状態を定義
export const initialState = {
fetchState: REQUEST_STATE.INITIAL,
goalsList: [],
};
fetchStateではAPIの状態を定義しています。
一覧取得の際の初期値は空配列です。
(2)リクエストのアクションを定義します
// リクエストのアクションを定義
export const goalsActionTypes = {
FETCHING: "FETCHING",
FETCH_SUCCESS: "FETCH_SUCCESS",
};
(3)Reducer関数を定義します
上記で設定したアクションの中身によって処理を分岐します。
API取得が成功したら、一覧データを返します。
// Reducerを定義
export const GoalsIndexReducer = (state, action) => {
switch (action.type) {
case goalsActionTypes.FETCHING:
return {
...state,
fetchState: REQUEST_STATE.LOADING,
};
case goalsActionTypes.FETCH_SUCCESS:
return {
fetchState: REQUEST_STATE.OK,
goalsList: action.payload.goals,
};
default:
throw new Error();
}
};
■画面に表示
一覧画面を表示したいコンポーネント上でuseReducerを使って、
先程作成したReducerを呼び出します。
export const Goals = () => {
const [state, dispatch] = useReducer(GoalsIndexReducer, initialState);
useEffect(() => {
dispatch({ type: goalsActionTypes.FETCHING });
displayGoals().then((data) =>
dispatch({
type: goalsActionTypes.FETCH_SUCCESS,
payload: { goals: data.goals },
})
);
}, []);
return (
<Flex align="center" justify="center" direction="column">
<Box bg="blue" margin="0" w="4xl">
<Card margin="30px" padding="40px" bg="green">
{state.goalsList.map((goal) => (
<TodaysGoalsList
key={goal.id}
goal={goal.goal}
reward_memo={goal.reward_memo}
reward_icon={goal.reward_icon_id_id}
achievement_icon={goal.achievement_icon_id_id}
timestamp={goal.created_at}
/>
))}
</Card>
</Box>
</Flex>
);
};
useReducer()に対して、第一引数にReducer、第二引数にinitialState(初期状態)を渡しています。
そして、その返り値を[state, dispatch]というかたちで受け取っています。
stateは状態、dispatchはアクションに該当します。
この時点のstateは初期状態です。
その後、displayGoals関数で一覧データの取得を行います。
データの取得が成功した後、dispatch関数を使ってReducerに通知します。
その際、
・アクションのtype(goalsActionTypes.FETCH_SUCCESS=データ取得成功)
・payload:取得したデータの内容
をReducerに渡しています。
Reducerはこれを受け取って処理を行い、stateを更新します。
そして、stateに格納されたデータを一覧に表示させます。
以上で実装完了です!
Discussion