React + Reduxを基本からまとめてみた【10】【Redux Toolkit ②】

##Redux とは

#####『Redux』 とは、JavaScript製の状態管理ライブラリ。
※ 「状態」とは、アプリケーションで扱う動的なデータという理解でよい。⇨　例えば、ユーザー名とか、いいねの数が「状態」で、状態は時系列とともに変化する。

状態管理ライブラリが解決する問題は、コンポーネントをまたいだデータの共有で、React でも Vue.js でも、コンポーネントは、DOM と同様にツリー構造をなす。
複数のコンポーネントで同じデータを使いたい、というケースがあるとする。
深い階層のコンポーネントがあるデータを必要とする場合、下図のように、実際にはそのデータを利用しないコンポーネントも含めて props のバケツリレーが発生する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/11cbe2dd-e9f2-8160-189a-f1136708670e.png)

これではアプリのコードを複雑にし、さらに途中のコンポーネントに無関係な props が定義される為、不必要な依存も産みやすい。
そこで考え出されたのが、状態管理ライブラリで、『ストア』と呼ばれるデータの入れ物を用意して、各コンポーネントが直接ストアとコミュニケーションし、これにより、上述の問題が解決される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/853e23ec-1cfb-b87e-a6ec-4b7de6140294.png)


##Redux の特徴
『Redux』の特徴は、コンポーネントからのアクセスに一定のルールが設けられている点である。
React におけるデータフローが props を通した親から子への流れに限定されているのも、コードをスパゲッティ化から守るための意図がある。
状態管理がグローバル変数のように濫用できてしまうと、コードにアンチパターンを仕込む結果になってしまう。

Redux は上記のような問題に配慮し、コードを予測可能にしてバグが出にくくするため、ルールを設けている。

さて、以下がコンポーネントと Redux ストアとのデータのやり取りを表した図です（{} はオブジェクト、f(x) は関数であることを示しています）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/af3b51d5-cee8-90c0-0ed9-cf4494f0bace.png)

##Reduxのモジュール

####State
状態を格納するオブジェクト。

```sample.js
{ count: 0 }
```

####Reducer
受け取った Action（後述）に応じて State を変更する関数。
正確には、新たな State を返却する関数。

```sample.js
function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 }
    case 'ADD':
      return { count: state.count + action.payload }
    default:
      return state;
  }
}
```

####Action
状態の更新を指示するオブジェクト。一般的に以下のプロパティを持ちます。

```sample.js
{ type: 'INCREMENT' } // typeがStateに対する操作を表す
{ type: 'ADD', payload: 3 } // payloadは任意の引数
```

####処理の流れ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/6afcad22-31f2-bc4c-6fc2-30686b7f935a.png)


#####1. コンポーネントが、Action Creator を呼び出して Action を取得する。
#####2. 取得した Action を Reducer に渡す。　※これを『dispatch』という。
#####3. Reducer は、渡された Action に応じて State を更新する。
#####4. コンポーネントは State に変更があれば、関連する UI を書き換える。

####設計のポイント
このようなルールの重要なポイントは、コンポーネントが自由に状態（State）を書き換えられない点です。状態を更新するためには必ず Reducer に更新処理を依頼する必要があり、さらに Reducer はあらかじめ決められた Action にしか反応しない。

状態に対して定義された変更しか加えられないこのルールは、予期しない更新処理を防ぎ、コードを予測しやすくする。

また、Action Creator も不確実性の排除に一役買って、Reducer に対しては決められた属性および値を持つ Action を渡さなければならない。type の値など間違った Action を渡さないように、Action Creator を介して Action を取得する。

##Redux Toolkit

####Redux 導入の課題

『Redux』は最小限の機能のため、導入する際は上で説明した各モジュールを自分で準備しなくてはいけない。

※ それらのコーディングは煩雑で、初学者にはハードルになり、ある程度習得したあとも、定型文のようなセットアップコードを書くのは大変で、また、アプリの規模によってはストアを適切に分割する必要がある。

『Redux Toolkit』は、上記の問題を解決するために開発されたライブラリである。

#####1. Redux ToolkitとReact-Reduxのパッケージをプロジェクトに追加。

```
$ npm install --save @reduxjs/toolkit react-redux
```

#####2. Reduxストアの作成
src/app/store.js という名前のファイルを作成する。
Redux ToolkitからconfigureStore APIをインポートし、まずは空のReduxストアを作成し、エクスポートするところから始める。

```app/store.js
//JavaScript
import { configureStore } from '@reduxjs/toolkit'

export const store = configureStore({
  reducer: {},
})

//TypeScript
import { configureStore } from '@reduxjs/toolkit'

export const store = configureStore({
  reducer: {},
})

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
```

これにより、Reduxストアが作成され、また、開発中にストアを検査できるように、
Redux DevToolsエクステンションが自動的に設定される。

#####3. ReduxストアをReactに提供
ストアを作成したら、src/index.jsにあるアプリケーションの周りにReact-Redux   Provider を置くことで、Reactコンポーネントから利用可能にすることができる。
先ほど作成したReduxストアをインポートし、Appの周りにProviderを配置し、propとしてストアを渡す。

```index.js
//JavaScript  //TypeScript
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
//追加
import { store } from './app/store'
import { Provider } from 'react-redux'

ReactDOM.render(
//追加
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)
```
#####4.ReduxのState Sliceを作成
src/features/counter/counterSlice.js という名前のファイルを新規に追加し、そのファイルで、Redux ToolkitからcreateSlice APIをインポートする。

スライスを作成するには、スライスを識別するための文字列名、状態の初期値、状態を更新する方法を定義するための1つ以上のreducer関数が必要で、スライスを作成すると、生成されたReduxのアクションクリエイターとスライス全体のレデューサー関数をエクスポートすることができる。

Reduxでは、データのコピーを作成し、そのコピーを更新することで、すべての状態の更新をイミュータブルに記述する必要がある。しかし、Redux ToolkitのcreateSliceとcreateReducerのAPIは内部でImerを使って、正しいimmutable更新となる「mutating」更新ロジックを書くことができるようになっている。

```features/counter/counterSlice.js
//JavaScript
import { createSlice } from '@reduxjs/toolkit'

const initialState = {
  value: 0,
}

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      // Redux Toolkit allows us to write "mutating" logic in reducers. It
      // doesn't actually mutate the state because it uses the Immer library,
      // which detects changes to a "draft state" and produces a brand new
      // immutable state based off those changes
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload
    },
  },
})

// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions

export default counterSlice.reducer

//TypeScript

import { createSlice, PayloadAction } from '@reduxjs/toolkit'

export interface CounterState {
  value: number
}

const initialState: CounterState = {
  value: 0,
}

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      // Redux Toolkit allows us to write "mutating" logic in reducers. It
      // doesn't actually mutate the state because it uses the Immer library,
      // which detects changes to a "draft state" and produces a brand new
      // immutable state based off those changes
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    incrementByAmount: (state, action: PayloadAction<number>) => {
      state.value += action.payload
    },
  },
})

// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions

export default counterSlice.reducer
```

#####5.ストアにスライスのリデューサを追加
次に、カウンタスライスからreducer関数をインポートして、ストアに追加する必要がある。reducerパラメータ内にフィールドを定義することで、その状態に対するすべての更新を処理するためにこのスライスreducer関数を使用するようにストアに指示する。

```app/store.js
//JavaScript  //TypeScript
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'

export default configureStore({
  reducer: {
    counter: counterReducer,
  },
})
```

#####6.ReactコンポーネントでReduxのStateとActionを使う

React-Reduxフックを使って、ReactコンポーネントがReduxストアと対話できるようになった。useSelector を使ってストアからデータを読み、useDispatch を使ってアクションをディスパッチすることができる。src/features/counter/Counter.js ファイルを作成して <Counter> コンポーネントを作成し、そのコンポーネントを App.js にインポートして <App> 内にレンダリングする。

```features/counter/Counter.js
//JavaScript
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'

export function Counter() {
  const count = useSelector((state) => state.counter.value)
  const dispatch = useDispatch()

  return (
    <div>
      <div>
        <button
          aria-label="Increment value"
          onClick={() => dispatch(increment())}
        >
          Increment
        </button>
        <span>{count}</span>
        <button
          aria-label="Decrement value"
          onClick={() => dispatch(decrement())}
        >
          Decrement
        </button>
      </div>
    </div>
  )
}

//TypeScript
import React from 'react'
import { RootState } from '../../app/store'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'

export function Counter() {
  const count = useSelector((state: RootState) => state.counter.value)
  const dispatch = useDispatch()

  return (
    <div>
      <div>
        <button
          aria-label="Increment value"
          onClick={() => dispatch(increment())}
        >
          Increment
        </button>
        <span>{count}</span>
        <button
          aria-label="Decrement value"
          onClick={() => dispatch(decrement())}
        >
          Decrement
        </button>
      </div>
    </div>
  )
}
```

これで、「増量」「減量」ボタンをクリックすることができる。

- 対応するReduxのアクションがストアにディスパッチされます。
- カウンタースライスのレデューサーがアクションを見て、状態を更新します。
- Counterコンポーネントは、ストアから新しい状態の値を見て、新しいデータで自分自身を再レンダリングする。

##学んだこと

- configureStoreでReduxストアを作成する
- configureStoreは、名前付き引数としてreducer関数を受け取ります。
- configureStoreは自動的に良いデフォルト設定でストアをセットアップする。
- ReduxストアをReactアプリケーションのコンポーネントに提供する。
- React-Redux の <Provider> コンポーネントを <App /> の周りに配置する。
- Reduxストアを<Provider store={store}>として渡す。
- Reduxの "スライス "レデューサーをcreateSliceで作成する。





##参考サイト
[HookとRedux ToolkitでReact Reduxに入門する]
(https://coders-shelf.com/react-router-v6-intro/)
[Redux Toolkit]
(https://redux-toolkit.js.org/tutorials/quick-start)