ReactにおけるRedux Toolkitの使い方 ✍🏻

# Reduxとは

Reactとは別の状態管理ライブラリです。Reactと一緒に使用されることが多いので、Reactの機能の一つだと思いがちですが、状態管理だけを担当するライブラリです🦾
なので他のjsライブラリと使用することもできます。
Reduxは難しい内容が多く、今はわからない部分はそういうお作法なんだと思った方がいいかもしれません..。
自分はそうしてます🙇‍♂️
一応、下記からRedux Application Data Flowでアプリ間でのデータの流れは確認できます。

https://redux.js.org/tutorials/fundamentals/part-2-concepts-data-flow

ReduxのReducerには副作用は書けません。Reducerは純粋関数である必要があります。

:::note
**純粋関数とは**
* 関数の戻り値が引数のみに依存する
* 外部スコープのデータは参照・変更しない
* 引数で渡された値を変更しない
* 関数外に影響を及ぼさない

上記要件を満たさない操作は副作用と呼ばれ、副作用のある関数は純粋関数ではない。
:::

## 使用例

下記のカウントアップコンポーネントを作成します。

![スクリーンショット 2023-01-27 22.48.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/7cb09abd-f5d7-7f31-8d2b-280663405d93.png)

useContextやuseReducerを理解している必要があります。

https://qiita.com/__knm__/items/69a4c6bf916a2d1ca9dd

https://qiita.com/__knm__/items/8f629d70369077bdd6d6


下記公式に従ってインストールします。

https://redux-toolkit.js.org/introduction/getting-started

ディレクトリ構造は下記です。

├ component/
　　├ Counter.js
　　├ CounterButton.js
　　└ CounterResult.js
├ store/
　　├ index.js
　　├ modules/
　　　　　└ counter.js

└ Example.js

```jsx:Example.js
import Counter from "./components/Counter";
import { Provider } from "react-redux";
import store from "./store"

const Example = () => {
  return (
    <Provider store={store}>
      <Counter />
    </Provider>
  );
};

export default Example;
```

useContextでグローバルな状態管理をする際には`Provider`というコンポーネントを使用し、その`value`にセットされた値がネストされたコンポーネントで取得することができるようなりますよね。
それと似たようなものです🙌
`redux`では`value`ではなく`store`属性で管理します。

```jsx:store / index.js
import { configureStore } from "@reduxjs/toolkit";
import reducer from "./modules/counter";
 // configureStore()はStoreを定義する関数。
 // reducer名と作成したreducerを渡す。
export default configureStore({
  reducer: {
    counter: reducer,
  },
});
```

その`store`属性に渡すグローバルで状態管理したい中身です。
`configureStore()`を使用し`Store`を定義します。`state`を格納し参照したりしておく保管庫のようなものですかね..🚪
`configureStore()`は引数にオブジェクトを取ります。
そのオブジェクトにreducerというプロパティを定義し、reducer名と作成したreducer（更新用関数）を設定しておきます。
渡しているreducerは下記のcounter.jsでexportしているreducerです。

```jsx:store / modules / counter.js
import { createSlice } from "@reduxjs/toolkit";

 // createSlice()はreducerを定義する関数。
 // グローバルで管理したいstateとreducerの処理内容(stateの値をどう更新するか)を書いていく。
const counter = createSlice({
  name: "counter", // このSliceの名前
  initialState: 0, // state初期値
  reducers: {
    // 定義したいメソッド群。このメソッドの引数にstateとアクションが渡ってくる
    add(state, { payload }) {
      return state + payload;
    },
    minus(state, { payload }) {
      return state - payload;
    },
  },
});


 // Actions Create(アクション部分の関数)はtoolkitを使用した場合自動で生成
const { add, minus } = counter.actions;
export { add, minus };

// configureStore()に渡すreducerとして、変数counterのreducerプロパティを export
export default counter.reducer;

```

`createSlice()`はreducerを定義する関数です。
こちらも引数にオブジェクトをとり、グローバルで管理したいstateとreducerの処理内容(stateの値をどう更新するか)を書いています。
toolkitを使用した場合、Actions Create(アクション部分の関数)は自動で生成されます🎉！
`createSlice()`を格納した変数`counter`の`actions`というプロパティから取得でき、それを分割代入とexportしています。
`counter`の`name`プロパティ、`reducers`プロパティのメソッド名から自動でアクションのtypeプロパティが生成され、それに紐づいたreducerが実行されます。
最後に`export default counter.reducer;`していますが、`configureStore()`の引数の中の`reducer`プロパティに渡す`reducer`として、`createSlice()`を格納した変数counterのreducerプロパティをexportしています。

```jsx: components / Counter.js
import CounterResult from "./CounterResult"
import CounterButton from "./CounterButton"

const Counter = () => {
    return (
        <>
            <CounterResult />
            <CounterButton step={2} calcType="+"/>
            <CounterButton step={2} calcType="-"/>
            <CounterButton step={10} calcType="+"/>
            <CounterButton step={10} calcType="-"/>
        </>
    )
}
export default Counter;
```
`props`で値を渡しています。この値をアクションに使用して、reducer()の処理内容を切り分けています。

```jsx: components / CounterButton.js
import { useDispatch } from "react-redux";
import { add, minus } from "../store/modules/counter"

const CounterButton = ({calcType, step}) => {
 
    // useDispatch()で、createSlice()の引数に渡したreducer取得
    const dispatch = useDispatch();
    
    const clickHandler = () => {
        const action = calcType === '+' ? add(step) : minus(step);
        dispatch(action);
    }

    return <button onClick={clickHandler}>{calcType}{step}</button>
}
export default CounterButton;
```

`useDispatch()`で、`createSlice()`の引数に渡した`reducers`が取得できます。
取得した`dispatch()`の引数にアクションを渡し、reducerを切り替えています。
`add(step)`と`minus(step)`は自動で生成されているActions Createです✏️
実行時に`type`を含むオブジェクトを返します。

```jsx:components / CounterResult.js
import { useSelector } from "react-redux";
const CounterResult = () => {
  const state = useSelector((state) => state);
  return <h3>{state.counter}</h3>;
};

export default CounterResult;
```

`useSelector()`でグローバルで管理しているstateが取得できます。
コールバック関数を持つ必要があり、今回はそのまま返しています。

これで`props`でのバケツリレーをすることなく、様々なコンポーネント間でデータのやりとりができているかと思います！🚀

### 処理のまとめ 📌

* /Example.js 👉 `Provider`の使用と`store`の設定。
* /store/index.js 👉 `configureStore()`を使用してStore定義。`reducer`の設定。
* /store/modules/counter.js 👉 `createSlice()`で`reducer`定義。グローバル状態管理する`state`、更新関数処理、アクションを記述。
* /components/Counter.js 👉 `CounterResult`、`CounterButton`コンポーネントを実行。 アクション用に`props`している。
* /components/CounterButton.js 👉 `useDispatch()`で`dispatch`の設定と、実行。設定した`reducer`の処理が行われる
* /components/CounterResult.js 👉 `useSelector()`でグローバル管理している`state`の取得と、表示。

## グローバル管理したいstateがオブジェクトだった場合

管理しているstateはプリミティブ型でしたがオブジェクトだった場合の書き方です。
Reduxの`reducer`も基本的には`useReducer`と同様に、純粋関数であることとイミュータビリティの保持をする必要があります。
今回において、イミュータビリティの保持とは簡単にいうと、オブジェクトをコピーして、そのコピーした新しいオブジェクトの値を変えて返すことです。

https://developer.mozilla.org/ja/docs/Glossary/Immutable

:::note
**イミュータブル**
書き換えが不可（元の値は変わらない）。
文字列や数値、真偽値、BigInt、undefined、シンボル

**ミュータブル**
書き換えが可能（元の値が変わる）。
イミュータブルな値以外。オブジェクトや配列。
:::

実際に`state`をオブジェクトにして試します。
下記のようにコードを変更します。

```diff_jsx:store / modules / counter.js
import { createSlice } from "@reduxjs/toolkit";

const counter = createSlice({
  name: "counter", 
- initialState: 0, 
+ initialState: {
+   count: 0,
+ },
  reducers: {
    add(state, { payload }) {
-     return state + payload;
+     const newState = { ...state }; // stateを展開して新しいオブジェクト作成
+     newState.count = state.count + payload;
+     return newState;
    },
    minus(state, { payload }) {
-     return state - payload;
+     const newState = { ...state }; 
+     newState.count = state.count - payload;
+     return newState;
    },
  },
});


const { add, minus } = counter.actions;
export { add, minus };

export default counter.reducer;
```

stateレンダリング部分も変更します。

```diff_jsx:components / CounterResult.js
import { useSelector } from "react-redux";
const CounterResult = () => {
-  const state = useSelector((state) => state);
-  return <h3>{state.counter}</h3>;
+  const count = useSelector((state) => state.counter.count);
+  return <h3>{count}</h3>;
};

export default CounterResult;
```

こちらで実行してみると、`state`がプリミティブ型だった時と同様に動作します🙌

### Toolkitではミュータブルな変更が許容される

まず大前提として、先述した通り、Reduxの`reducer`に関しても純粋関数である必要があり、上記ではイミュータビリティを保持した書き方をしました。
ですが実は、Toolkitの中ではミュータブルな操作が、Immerというライブラリによって内部的にはイミュータブルに取り扱われるそうです..🤔

:::note
ミュータブルな操作とは引数で渡ってきたオブジェクトのプロパティを書き換えてしまう操作のことを指します。
:::

純粋関数の中では基本的にこのような操作をしてはいけないのですが、Redux Toolkitでは許可されます。
実際にコードを書き換えてみます。

```diff_jsx:store / modules / counter.js
import { createSlice } from "@reduxjs/toolkit";

const counter = createSlice({
  name: "counter", 
  initialState: {
    count: 0,
  },
  reducers: {
    add(state, { payload }) {
-     const newState = { ...state };
-     newState.count = state.count + payload;
-     return newState;
+     state.count = state.count + payload;
    },
    minus(state, { payload }) {
-     const newState = { ...state };
-     newState.count = state.count - payload;
-     return newState;
+     state.count = state.count - payload;
    },
  },
});


const { add, minus } = counter.actions;
export { add, minus };

export default counter.reducer;
```

こちらで実行すると、追加したコードのようなミュータブルな操作をしても正常に動作することがわかります。
注意としてはこのようなミュータブルな変更を加えたオブジェクトを、　新しい`state`にしたい場合は`return`を書いてはいけません。