*この記事は、あくまで現時点で知っている情報をまとめているに過ぎないので、参考程度に留めておいてください。
Reduxの開発チームが効率的で快適なDXを目指して開発されたライブラリです。
Reduxは、最小限の機能しか持っていないため、導入するためには、役割ごとに各モジュールを自身で準備しないといけません。
それは、たとえ、Reduxに習熟したとしても、必要になる定型文のようなコードをいちいち書かなければいけない事を意味します。また、アプリの規模が大きくなってくると、様々な種類のデータをどのように持つか、Storeの分割をどのようにするかということも、各自、各チームで判断しないといけなくなります。(いくつかのパターンが提案されているのみ)
上記の問題を解決する上で、Redux Toolkitを導入することは、生産性の向上に繋がるとして、注目されているように感じます。
主要なAPIは以下の通りです。
各モジュールの生成やStoreの分割方法をヘルパー関数が用意されており、Reduxの導入を手助けしてくれます。
configureStore
各種デフォルト値が設定可能なcreateStateのカスタム値
createReducer
reducerの作成を簡単にしてくれる
(SliceのReducerを結合する際に使うケースが多いかも)
createAction
action creatorを作成する
(createSliceを使うので、使うケースは少ないかも)
createSlice
actionの定義とaction creator, reducerをまとめて生成できる
TypeScript導入していないシンプルな例で、Redux Toolkitを導入した書き換えをしてみます。 導入時のコマンドは以下の通りです。
npm install --save @reduxjs/toolkit react-redux
Redux Toolkit導入前
export const increment = () => ({
type: "INCREMENT"
})
export const decrement = () => ({
type: "DECREMENT"
})
Redux Toolkitで書き直したもの
import { createAction } from '@reduxjs/toolkit';
const FEATURE = 'counter';
export const incremented = createAction(`${FEATURE}/increment`);
export const decremented = createAction(`${FEATURE}/decrement`);
Redux Toolkit導入前
export const initialState = { count: 0 };
export const countReducer = (state = initialState, action) => {
switch(action.type) {
case 'INCREMENT' :
return {
...state, count: state.count + 1
};
case 'DECREMENT':
return {
...state, count: state.count -1
}
default: {
return state;
}
}
}
Redux Toolkitで書き直したものです。 Switch文を書かずに、すっきり書けます。
import { createReducer } from '@reduxjs/toolkit';
import { incremented, decremented } from './counter-action';
export type CounterState = {
count: number;
}
const initialState: CounterState = { count: 0 };
export const counterReducer = createReducer(initialState, {
[incremented.type]: (state) => ({...state, count: state.count + 1}),
[decremented.type]: (state) => ({...state, count: state.count - 1}),
})
上記を__createSlice__を使ってさらに短く書くと、以下のようになります。
actionとreducerのロジックを統合したものを、__Slice__と呼びます。
構成にもよるかと思いますが、Sliceとは、__Store全体を構成する一部分のStore__を意味します。
例えば、ECサイトなどで考えると、User、Item、Cartなど、状態の内容や用途別に、State、Action Creator、Reducerを切り分けてまとめることで、コードの見通しを良くしてくれます。
createSlice関数は、State、Action Creator、Reducerをまとめて作成することが出来る関数です。即ち、上記で触れたActionsとReducerは、createSlice関数で生成することが可能です。
import { createSlice } from '@reduxjs/toolkit';
export type CounterState = {
count: number;
}
const initialState: CounterState = { count: 0 };
export const counterSlice = createSlice({
name: 'counter', // Sliceの名前
initialState, // 初期State
reducers: { // Reducer(=StoreのStateに対して更新処理を定義する役割)
// 以下のKeyがAction Creatorの関数名=Action Creatorが生成される。
// 以下コードでは第一引数に現在のStateのみ渡してますが、第二引数は渡されたactionを記述
incremented: (state) => ({ ...state, count: state.count + 1 }),
decremented: (state) => ({ ...state, count: state.count - 1 }),
}
})
上記によって作成されたSliceは、以下のプロパティを持つオブジェクトとなります。
const hogeSlice = createSlice(/* ... */);
hogeSlice.name;
hogeSlice.reducer;
hogeSlice.actions;
例えば、上記で触れたECサイトを開発する際に、User、Item、Cartなど、それぞれcreateSliceで作成したReducerは一つにまとめた上で、Storeを生成しなければなりません。その場合は、以下のような記述になるかと思います。
import { combineReducers } from "redux";
import { configureStore } from "@reduxjs/toolkit";
// createSliceで生成したReducer
import userReducer from "./user";
import itemReducer from "./item";
import cartReducer from "./cart";
// combineReducers関数でReducerを結合
const reducer = combineReducers({
user: userReducer,
item: itemReducer,
cart: cartReducer
});
// configureStoreに結合したReducerを渡して、Storeを生成
const store = configureStore({ reducer });
export default store;
Redux Toolkit導入前
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { initialState, reducer } from './Reducer/reducer';
const store = createStore(reducer, initialState);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
Redux Toolkitで書き直したもの createSlice関数で生成されたReducerを渡してます。
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import store from './store'; // 結合したReducerが渡されているStore
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
Redux Toolkit導入前
import React from 'react';
import { increment, decrement } from 'Action/actionCreator';
import { useSelector, useDispatch } from 'react-redux';
function App() {
const count = useSelector(state => state.count);
const dispatch = useDispatch()
return(
<>
<div>{count}</div>
<button onClick={()=> dispatch(increment())}>click</button>
<button onClick={()=> dispatch(decrement())}>click</button>
</>
)
}
export default App;
Redux Toolkitで書き直したもの
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { counterSlice } from 'features/counter/counter';
function App() {
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
const { incremented, decremented } = counterSlice.actions;
return(
<>
<div>{count}</div>
<button onClick={()=> dispatch(incremented())}>click</button>
<button onClick={()=> dispatch(decremented())}>click</button>
</>
)
}
export default App;
以上、簡単にですが、Redux Toolkitの紹介でした。 これは、沢山書いて、慣れるしかなさそう。。。