Redux Toolkit

Redux Toolkitとは(React環境での解説になります)

ざっくり言うと、「データの状態」と「データの処理方法」をまとめて管理、記述できるもの。
まずは、Store、Slice という概念があります。

 

■Store
Slice全体を管理しているでっかい倉庫(Amazon)

倉庫

■Slice
各種データを管理している区域の様なもの

■まずは「データの状態」だけでReduxの中身を見てみる
Redux Toolkit の中身について、ほぼフォルダ構造としてイメージ出来るので理解しやすいと思います。

 

// テキストで書き出すと以下の様なイメージ
store // Slice全体を管理しているでっかい倉庫(Amazon)
├ book // Sliceの名前で、区分している(区域、フォルダの様なもの)
└ camera //  


// これをもっと階層を掘り下げるとこんな感じでデータが管理されてます。
store
├ book
│ itemList: {
│		{id: 1, name: 'React入門'},
│		{id: 2, name: 'Redux Toolkit入門'},
│	}
└ camera
    itemList: {
        {id: 1, name: 'Nikon D850'},
        {id: 2, name: 'Nikon D800'},
    }

 

■sliceに書き起こしてみる

// Book.ts
import { createSlice } from "@reduxjs/toolkit";

// 初期値として入れたいデータ
// 本来はAPIで取得したDBのデータを格納するが、ここではダミーを作成して説明
const bookItems = {
    {id: 1, name: 'React入門'},
    {id: 2, name: 'Redux Toolkit入門'},
}

// stateの初期状態(このSliceで扱う全データの初期状態)
// stateとは、データ全体、オブジェクトで、stateを更新するにはreducerを使う。
const initialState = {
  itemList: bookItems
};

// Sliceを生成する
const bookSlice = createSlice({
  name: "book", // Sliceの名前
  initialState, // データの初期状態(上記のinitialStateがここで適応される)
  reducers: {   // データの処理について記述
  // 一旦省略
  }
});

// Reducerをエクスポートする
export default bookSlice.reducer;
// Camera.ts
import { createSlice } from "@reduxjs/toolkit";

const cameraItems = {
    {id: 1, name: 'Nikon D850'},
    {id: 2, name: 'Nikon D800'},
}

const initialState = {
  itemList: cameraItems
};

const cameraSlice = createSlice({
  name: "camera",
  initialState,
  reducers: {
  // 一旦省略
  }
});

// Reducerをエクスポートする
export default cameraSlice.reducer;

■SliceをStoreに読み込む

import { combineReducers } from "redux";
import { configureStore } from "@reduxjs/toolkit";

// それぞれ slice.reducer を default export している前提
import bookReducer from "./book";
import cameraReducer from "./camera";

const reducer = combineReducers({
  book: bookReducer,
  camera: cameraReducer
});

const store = configureStore({ reducer });

export default store;
■結果
以下の様なイメージでStore(倉庫)の中のSlice(区域)にデータが入り、管理されます。
区域には、各ジャンル別にデータ(在庫)を置いているイメージです。
store
    book:
        itemList: {
            {id: 1, name: 'React入門'},
            {id: 2, name: 'Redux Toolkit入門'},
        }
    camera:
        itemList: {
            {id: 1, name: 'Nikon D850'},
            {id: 2, name: 'Nikon D800'},
        }

ここまでが「データの管理」までのざっくりとしたイメージです。

reducers

次に、処理の話です。
ここから少しだけ作業が増えますが、ゆっくり辿って確認していきましょう。
先に動きの結果だけ言うと
① reducersは、データ処理後の結果をstateに反映する
② stateが反映されるとstoreが更新される
③ sotreが更新されるとコンポーネントに反映される。

 

↓倉庫で例えると

 

① レジで清算を行う → 減った在庫をstateに反映する
② stateが更新される → 在庫データをstoreに反映する
③ storeが更新される → どのレジからsotreにアクセスしても在庫が反映されている状態を確認出来る
■処理イメージ
■処理
// Book.ts
import { createSlice } from "@reduxjs/toolkit";

const bookItems = {
    {id: 1, name: 'React入門'},
    {id: 2, name: 'Redux Toolkit入門'},
}

const initialState = {
  itemList: bookItems
};

const bookSlice = createSlice({
  name: "book",
  initialState,
  reducers: {
  updateItemList: (state, action) => {
		// state.itemListは、initialStateの中のitemListの事をさしています。
		// action.payloadは、state.itemListの中身に対して更新したい内容です。
      state.itemList = action.payload
    }
  }
});

export default bookSlice.reducer;
上記のコメントにもあるように「updateItemList」でstate(データ/オブジェクト)に更新をかけています。
そして、reduxの処理はこれで完了です。
では、在庫が増えた、減ったの処理はどうするのか?
それは、レジ = コンポーネント側で処理します。(本来は、処理専用の関数ファイルを用意しますが、ここではコンポーネントにまとめて説明します)
そして、その処理は普通のJavaScriptです。

 

■コンポーネント側で関数作って処理
では早速処理と言いたい所ですが、処理をする前に、在庫が分からないと処理ができません。
ですので、レジ = コンポーネントに、sotreを経由して在庫を読み込み、処理してみましょう。
// useSelectorは、Sliceの中身を読み込むためのもの
// useDispatchは、Sliceのreducrsに処理の実行を依頼するために必要なもの
import { useSelector, useDispatch } from "react-redux";

// Book.tsのreducersを読み込む
import { updateItemList } from "../store/slice/Book";

const Component = () => {
    // dispatchとして定数に設定
  const dispatch = useDispatch();
    // useSelectorを使ってsliceで管理しているデータを読み込む
    const imteList = useSelector((state: RootState) => state.Book)

  const deleteItem = () => {
        // JavaScriptのpop()を使って配列の最後を削除
        itemList.pop()
        // dispatchを使って更新を×
        dispatch(updateItemList(itemList))	
    }

  return (
        <button onClick={deleteItem}>click</button>
  );
}
ここまで来ると、あとは普通にJavaScriptで処理しているだけです。
一応、流れを書き出すと
「const imteList = useSelector((state: RootState) => state.Book)」で、BookSliceで設定したitemLsitのデータが読み込まれます。
ここの解説で表すといかのデータです。
BookSlice
itemList:
{ 
    {id: 1, name: 'React入門'},
    {id: 2, name: 'Redux Toolkit入門'},
}
そして「Component」のボタンを押すと「deleteItem」が実行されます。
「deleteItem」は、配列の最後を削除するという事なので、1回押せば次のように配列が変わります。
{ 
    {id: 1, name: 'React入門'},
}

で「dispatch(updateItemList(itemList))」でSliceのreducersの「updateItemList」が実行されます。

updateItemList: (state, action) => {
    state.itemList = action.payload
}
action.payload には、deleteItemで処理された「{id: 1, name: 'React入門’}」が渡されます。
そして、state.itemList = action.payload としてるので、
itemListの中身は「{id: 1, name: 'React入門’}」に書き換わります。

 

ここれで、データの更新が完了されました!

 

しかし、ここまでではありません。最後にSliceが更新されると
Storeに反映されBookSliceを読み込んでいるコンポーネントに
再レンダリングがかかり、コンポーネントも再描画されます。
ここまでが一連の流れです。
ここでは、ざっくり概要を掴みたい人に色々省略して書いたものです。
より詳しく知りたい方は、以下が参考になります。