- 参考
- 公式のありがたい画像
- 画像に出てくる各用語の解説
- createAsyncThunkとは
- createSliceとは
- サンプルコード
- ちょっと分かりづらい所メモ(ActionCreatorとtype)
参考
公式のありがたい画像
公式のはGIF画像なので下記画像よりわかりやすい。要参照。
Redux Fundamentals, Part 6: Async Logic and Data Fetching | Redux
画像に出てくる各用語の解説
詳しくは下記に書いた。
Store
状態(State)を保管しておく場所。
Reducer
Reducerとはstateをどのように更新するか定義した関数。
State
状態のこと。
Reduxは、このStateを管理するためのライブラリ。
Stateが更新されると画面が再描画される。
UI
関数コンポーネントのこと。
Action
名前はActionだけど何のActionもしない。
Reducerの識別子である「tpye」と「payload(Stateの素)」を持つオブジェクト。
payload
本来のデータ部分のことらしい。
HTTPで言うと、ヘッダデータなどの付加情報をを除いたボディ部分。
APIとの通信の場合、payloadにレスポンスデータが入ってくる。
Dispatch
ReducerにActionを渡して、Reducerを呼び出す関数。ここがスタート。
dispatch(action);
でReducer呼び出し。
EventHandler
そのまま。
何らかのEventと紐付ける。ボタンクリックなど。
StoreとEventHandlerの繋ぎ
Dispatchの引数にActionオブジェクトを渡して繋ぐ。
ActionオブジェクトにはReducerの識別子であるtypeプロパティがある。
typeプロパティの値でReducerと紐付ける。
createAsyncThunkとは
Redux Tool kitに含まれる非同期処理を扱うためのツール。
Stateの更新を非同期で行う場合に使う。
APIとの通信によく使われます。
ざっくり言うとこの関数の引数に、Promiseを返す非同期処理を渡すとStateを更新できる。
createSliceとセットで使う。
createAsyncThunkの構文
createAsyncThunk( 'type', 非同期関数 ) const addAsyncWithStatus = createAsyncThunk( 'counter/asyncCount', // 第一引数 = type = 一意な名前なら何でもよい // 第2引数 = 何らかの非同期処理↓ async (payload) => { const response = await asyncCount(payload); return response.data; } )
createAsyncThunkの引数
・第一引数:type(文字列)
Reducerを識別するための文字列。一意な名前なら何でもよいらしい。
・第二引数:payloadCreator
Promiseを返す非同期関数のこと。
createAsyncThunkの戻り値
ActionCreatorを返す。
返すActionCreatorは処理状態によって異なります。
- pendingの時: ‘関数名.pending’
- fulfilledの時: ‘関数名.fulfilled’
- rejectedの時: ‘関数名.rejected’
構文のコード例で行くと下記のActioCreatorが返ってくる。
addAsyncWithStatus.pending addAsyncWithStatus.fullfilled addAsyncWithStatus.rejected
createSliceとは
createSlice は、Redux Toolkit の一部であり、Reduxの createReducer や createAction を組み合わせたものです。
これにより、簡単にReduxのstoreの状態を更新するためのリデューサーとアクションを作成できます。
簡単に言うと、Reducer・ActioCreator・Stateを持つオブジェクトを返す。
構文
const counter = createSlice({ name: 'Slice名', //これがtypeの一部になる initialState: {state: '初期値'}, //reducersには同期処理 reducers: { /*Stateを更新する同期処理をここに書く*/ }, //extraReducersには非同期処理 extraReducers: (builder) => { builder.addCase(addAsyncWithStatus.pending, (state) => { //State更新する処理(処理中) }) .addCase(addAsyncWithStatus.fulfilled, (state, action) => { //State更新する処理(成功時) state.count = state.count + action.payload; }) .addCase(addAsyncWithStatus.rejected, (state) => { //State更新する処理(失敗時) }) } });
引数
- name=Sliceの名前
- initilState=stateの初期値
- reducers=同期処理のReducer
- extraReducers=非同期処理のReducer
戻り値
公式より引用
createSlice | Redux Toolkit
{ name: string, reducer: ReducerFunction, actions: Record<string, ActionCreator>, caseReducers: Record<string, CaseReducer>. getInitialState: () => State, reducerPath: string, selectSlice: Selector; selectors: Record<string, Selector>, getSelectors: (selectState: (rootState: RootState) => State) => Record<string, Selector> injectInto: (injectable: Injectable, config?: InjectConfig & { reducerPath?: string }) => InjectedSlice }
とりあえずactionCreatorとReducerを返すとだけ覚えておけばOK。
actionCreatorとReducerの取得方法↓
const sliceSample = createSlice( /*省略*/); const [ reducersオブジェクト内の関数名 ] = sliceSample.actions; // reducersのActionCreator取得 const rducerFunc = sliceSample.reducer; // Reducer取得。これをStoreに渡す
サンプルコード
非同期ボタンを押すと、0~1秒後に計算結果が返ってくるボタン。(extraReducers)
他のボタンは同期処理。(reducers)
・Example.js
import Counter from "./components/Counter"; import { Provider } from "react-redux"; // import store from "./store" import { createSlice, createAsyncThunk, configureStore } from "@reduxjs/toolkit"; //非同期処理用の関数。0~1秒後に引数の値を返すだけ。 //createAsyncThunkにthunkされる関数 //これの戻り値がextraReducersのaction.payloadに渡される const asyncCount = (count = 1) => { return new Promise((resolve) => setTimeout(() => resolve({ data: count }), Math.random() * 1000) ); }; //ActionCreator //createAsyncThunkはActioCreatorを返す関数 const addAsyncWithStatus = createAsyncThunk( 'counter/asyncCount', // 第一引数=type async (payload) => { // 第2引数=payloadCreator=何らかの値を返す非同期処理の関数 const response = await asyncCount(payload); return response.data; } ) //ActionCreator //createAsyncThunk2つめ。中身は同じ。 const addAsyncWithStatus2 = createAsyncThunk( 'test/asyncCount', async (payload) => { const response = await asyncCount(payload); return response.data; } ) /**Reducer&Action&Stateの生成 * createSliceはReducerとActionCreatorとStateを含むオブジェクトを返す */ const counter = createSlice({ name: 'counter',///Slice名。これがtypeの一部になる initialState: {//Stateの初期値 count: 0, status: '' }, //reducersには同期処理 reducers: { add(state, { type, payload }) { state.count = state.count + payload; }, minus(state, { type, payload }) { state.count = state.count - payload; } }, //extraReducersには非同期処理 extraReducers: (builder) => { builder.addCase(addAsyncWithStatus.pending, (state) => { state.status = 'Loading...' }) .addCase(addAsyncWithStatus.fulfilled, (state, action) => { state.status = '取得済' state.count = state.count + action.payload; }) .addCase(addAsyncWithStatus.rejected, (state) => { state.status = 'エラー' }) } }); //2つ目のcreateSlice //Reducer&Action&Stateの生成 const testSlice = createSlice({ name: 'test',//Slice名。これがtypeの一部 initialState: { result: 1, status2: '' }, reducers: { multiplication(state, { type, payload }) { state.result = state.result * payload; }, division(state, { type, payload }) { state.result = state.result / payload; } }, extraReducers: (builder) => { builder.addCase(addAsyncWithStatus2.pending, (state) => { state.status2 = "処理中"; }) .addCase(addAsyncWithStatus2.fulfilled, (state, action) => { state.status2 = "成功"; state.result = state.result * action.payload; }) .addCase(addAsyncWithStatus2.rejected, (state) => { state.status2 = "失敗"; }) } }); //Store生成 const store = configureStore({ reducer: { counter: counter.reducer,//counter.reducerにReducerが入っている test: testSlice.reducer//testSlice.reducerにReducerが入っている } }); // actionsにActionCreatorが入っている // typeが counter/addとcounter/minus const { add, minus } = counter.actions; // typeが test/multiplicationとtest/division const { multiplication, division } = testSlice.actions; const Example = () => { return ( <Provider store={store}> // CounterコンポーネントにActioCreatorをすべて渡す <Counter actionCreator1={add} actionCreator2={minus} actionCreator3={addAsyncWithStatus} actionCreator4={multiplication} actionCreator5={division} actionCreator6={addAsyncWithStatus2} /> </Provider> ); }; export default Example;
・Counter.js
import { useSelector, useDispatch } from "react-redux" //ExampleコンポーネントからActioCreatorをすべて受け取り const Counter = ({ actionCreator1, actionCreator2, actionCreator3, actionCreator4, actionCreator5, actionCreator6 }) => { const dispatch = useDispatch(); const clickHandler = (e) => { if (e.target.name === "+2") { dispatch(actionCreator1(2)); // dispatch(actionCreator)でReducerを呼び出す } else if (e.target.name === "-2") { dispatch(actionCreator2(2)); } else { dispatch(actionCreator3(2)); } } const clickHandler2 = (e) => { if (e.target.name === "*2") { dispatch(actionCreator4(2)); } else if (e.target.name === "/2") { dispatch(actionCreator5(2)); } else { dispatch(actionCreator6(3)); } } // useSelector()でStateを取得 const status = useSelector(state => state.counter.status); const count = useSelector(state => state.counter.count); const result = useSelector(state => state.test.result); const status2 = useSelector(state => state.test.status2); return ( <> <p>{count}</p> <h3>{status}</h3> <button onClick={clickHandler} name="+2">+2</button> <button onClick={clickHandler} name="-2">-2</button> <button onClick={clickHandler} name="非同期+2">非同期+2</button> <p>{result}</p> <h3>{status2}</h3> <button onClick={clickHandler2} name="*2">×2</button> <button onClick={clickHandler2} name="/2">÷2</button> <button onClick={clickHandler2} name="非同期*3">×3</button> </> ) } export default Counter;
ちょっと分かりづらい所メモ(ActionCreatorとtype)
ActionCreator
Actionとはtypeとpayloadを持つだけの単なるオブジェクト。
このActionオブジェクトを返すのがActionCreatorという関数。
const actionCreator = () => { return { type: "couter/add", // typeはReducerを特定するための識別子 paylod: 2 // payloadをReducerに渡して、これを元にStateを更新する } }
■ActionCreatorの取得
同期処理のActionCreatorはcreateSliceの戻り値から取得。
非同期処理のActionCreatorはcreateAsyncThunkの戻り値から取得。
// createSliceからActionCreator関数を取得 const counter = createSlice(/*省略*/); const { add, minus } = counter.actions; // createAsyncThunkからActionCreator関数を取得 // 非同期処理関数の戻り値(pending/fullfilled/rejected)に応じた下記のActionCreatorを自動生成 // addAsyncWithStatus.pending // addAsyncWithStatus.fullfilled // addAsyncWithStatus.rejected const addAsyncWithStatus = createAsyncThunk('type', 非同期処理関数);
reducersとextraReducersのtypeの違い
■reducersのtype(識別子)は「Slice名/Reducer名」
- counter/add
- counter/minus
- test/multiplication
- test/division
createSliceの戻り値であるActionCreatorにtypeが含まれている。
const { add, minus } = counter.actions; // typeはcounter/addとcounter/minus
■extraReducerのtype(識別子)は「createAsyncThunkの第一引数(type)/状態」
- counter/addAsyncWithStatus/pending
- counter/addAsyncWithStatus/fulfilled
- counter/addAsyncWithStatus/rejected
- test/addAsyncWithStatus2/pending
- test/addAsyncWithStatus2/fulfilled
- test/addAsyncWithStatus2/rejected
createAsycThunk関数の戻り値がActionCreator。
状態によってpending/fullfilled/rejectedが自動生成される。
const addAsyncWithStatus = createAsyncThunk('type', 非同期処理関数);