Redux Toolkitの便利関数

configureStore

通常、ReduxStoreを作成するには、createStore()を呼び出してReducer関数を渡していましたが、Redux Toolkitでは、 configureStore() を使用します。

違いは、複数の引数を渡すのでなく、名前付きフィールドを持つ単一のオブジェクトを渡す点です。reducerという名前のフィールドとして渡す必要がある点に注意が必要です。

//Redux
const store = createStore(counter)

//Redux Toolkit
const store = configureStore({
  reducer: counter
})

createAction

createActionは引数にActionTypeの文字列を受け取り、その文字列で、Action Creator関数を返します。既存の方法でAction Creatorを作るよりも、短くシンプルに書くことができます。

function createAction(type, prepareAction?)
const INCREMENT = "INCREMENT";
const DECREMENT = "DECREMENT";

function incremnetOriginal() {
  return { type: INCREMENT };
}

function decrementOriginal() {
  return { type: DECREMENT };
}

console.log(incremnetOriginal(), decrementOriginal());
// { type: 'INCREMENT' } { type: 'DECREMENT' }
// createAction関数を使うことによって上記と同じ処理を短くかけます。
const incrementNew = createAction<number | undefined>('INCREMENT');
const decrementNew = createAction<number | undefined>('DECREMENT');

console.log(incremnetNew(), decrementNew());
// { type: 'INCREMENT' } { type: 'DECREMENT' }

ReducerでActionTypeの文字列を参照する場合は、以下2パターンの方法で書くことができます。

const incremnet = createAction<number | undefined>('INCREMENT');

// ①
console.log(incremnet.toString()); // "INCREMENT"
// ②
console.log(increment.type); // "INCREMENT"

Action Creatorは引数無しで呼び出すことも、ActionにアタッチするPayloadを指定して呼び出すことも出来ます。

let action = increment();
// { type: 'INCREMENT' }

action = increment(10);
// { type: 'INCREMENT', payload: 10 }

prepare callbackについて

デフォルトでは、生成されたAction Creatorは単一の引数を受け入れ、それが、action.payloadになります。しかし、Action Creatorの複数のパラメータを受け入れたり、ランダムなIDを生成したり、タイムスタンプを取得したりするなど、payloadの生成をカスタマイズするために、追加のロジックを書きたい場面があったりします。これを実現するためには、createAction関数の第二引数に、payload値を構築するために使用される、__prepare callback__を指定することができます。

prepare callbackが指定された場合、Action Creatorの全ての引数は、prepare callbackに渡され、payloadフィールドの持つオブジェクトを返します。

import { createAction, nanoid } from '@reduxjs/toolkit';

const addTodo = createAction('todos/add', function prepare(text: string){
  return {
    payload: {
      text,
      id: nanoid(),
      createdAt: new Date.toISOString(),
    }
  }
});

console.log(addTodo('Study for App'))
/**
*{
*  type: 'todos/add',
*  payload: {
*    text: 'Study for App',
*    id: '4JIureoiO104Yijd',
*    createdAt: 2020-10-11T07:53:36.581Z'
*  }
*}
**/

matchメソッドについて

生成されたAction Creatorは__.match(action)メソッド__を持っています。
これは、渡されたActionがActionCreatorによって作成されたActionと同じTypeかどうかを判定するために使用することが可能です。

import { createAction, Action } from '@reduxjs/toolkit'

const increment = createAction<number>('INCREMENT')

function someFunction(action: Action) {

  if (increment.match(action)) {

  }
}

createReducer

通常、ReduxのReducerは、action.typeフィールドをチェックして、ActionTypeごとに特定のロジックを実行していました。

function counter(state = 0, action) {
  switch (action.type) {
    case increment.type:
      return state + 1
    case decrement.type:
      return state - 1
    default:
      return state
  }
}

Redux Toolkitでは、createReducer関数を使用して以下のように書くことができます。ActionTypeの文字列をKeyとして使用する必要があるので、ES6の「computed property」構文を使用して、ActionType文字列からKeyを作成することが出来ます。

const increment = createAction('INCREMENT');
const decrement = createAction('DECREMENT');

const counter = createReducer(0, {
  [increment.type]: state => state + 1,
  [decrement.type]: state => state - 1
})

上記の書き方以外に以下のような書き方も可能です。 computed propertiesは、内部にあるどのような変数に対してもtoString()を呼び出すので.typeフィールドを使用せずに、ActionCreator関数を直接使用することも可能です。

const counter = createReducer(0, {
  [increment]: state => state + 1,
  [decrement]: state => state - 1
})

createSlice

上記でAction Creatorを個別に作成していますが、createSliceを使用する事によって、State,Reducer,Action Creatorはまとめて作成することが可能です。

createSliceは、reducerとactionsを格納したsliceオブジェクトを返します。 reducerフィールドには、生成された関数、actionsフィールドには生成されたAction Creatorを格納しています。

createSliceは、以下のオプションを持つオブジェクトパラメータを受け取ります。

function createSlice({
    // ActionTypeでプレフィックスとして使用される名前
    name: string,
    // Reducerで使用される初期値
    initialState: any,
    // reducersオブジェクト。Key名からActionを生成します。
    reducers: Object<string, ReducerFunction | ReducerAndPrepareObject>
    // Reducerを追加するために使用されるコールバックまたは、reducersの追加オブジェクト。
    extraReducers?:
    | Object<string, ReducerFunction>
    | ((builder: ActionReducerMapBuilder<State>) => void)
})

reducersフィールドは、__casereducer関数(特定のActionTypeを扱うことを目的とした関数で、Switch構文内のcase文に相当__を含むオブジェクトです。KeyはActionTypeを生成するために使用されます。

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    increment: state => state + 1,
    decrement: state => state - 1
  }
})

const store = configureStore({
  reducer: counterSlice.reducer
})

document.getElementById('increment').addEventListener('click', () => {
  store.dispatch(counterSlice.actions.increment())
})

大体は、Destructuring assignmentを使用して、以下のように、Action CreatorやReducerを取り出すことになります。

const { actions, reducer } = counterSlice;
const { increment, decrement } = actions;