React Redux Tutorial Part 1 -- react-redux の導入 と reduxjs/toolkit の createSlice を使った counter アプリの作成
Tags: react, redux
Published: 2022-04-27 17:28:40


## why

* 次のプロジェクトで必要とされるから。
* 以前のプロジェクトで携わったが、reducer や useSelector などを理解できてないままなんとなくで使ってしまっていたから。
* カスタムフックを作れるようになりたいから。

---

## 何をやるのか

https://react-redux.js.org/tutorials/quick-start

React-Redux の公式チュートリアル、クイックスタート

React アプリに redux toolkit の slice を導入して
グローバルステートの count の値をボタンで上下させるプロジェクトを作る。


## プロジェクト作成とライブラリのインストール

```ts
npx create-react-app redux
```

これで React のプロジェクトディレクトリを作成

https://react-redux.js.org/tutorials/quick-start

この react-redux 公式チュートリアル通りに

```ts
npm install @reduxjs/toolkit react-redux
```

redux のツールキットと react-redux 
これらの npm ライブラリをインストールする

---

## app/store に store ファイルの作成する

https://react-redux.js.org/tutorials/quick-start#create-a-redux-store

この react-redux 公式チュートリアル通りに

src/app/store.js

にストアファイルを作ってみる

```ts
import { configureStore } from '@reduxjs/toolkit'

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

toolkit のライブラリから configureStore というものをインポートする

configureStore の名前で export する。
中身の reducer はまだない。

普通の redux の combine のようなものだと推測する。

---

## index.js で store ファイルをインポートして Provider に繋げる。

```ts
import store from './app/store'
import { Provider } from 'react-redux'
```

store ファイルをインポートして
react-redux のライブラリから Provider をインポートする

```ts
  <React.StrictMode>
    <App />
  </React.StrictMode>
```

デフォルトでは StrictMode で App が括られているが

```ts
  <Provider store={store}>
    <App />
  </Provider>
```

今回は Provider で括るようにする。

---

## npm start で起動する

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n0tqk19b3loz8unktpml.png)
 
この store と Provider を入れた React アプリを起動すると普通に動く。

```ts
redux.js:426 Store does not have a valid reducer. 
Make sure the argument passed to combineReducers is 
an object whose values are reducers.
warning @ redux.js:426
```

reducer 何もないぞって警告がコンソールに出ている。

---

## features/counter/counterSlice に redux state と reducer がまとまった slice ファイルを作る

https://react-redux.js.org/tutorials/quick-start#create-a-redux-state-slice

src/ に features/counter/ というフォルダを作り

counterSlice.js というファイルを作る

```ts
import { createSlice } from '@reduxjs/toolkit'
```

redux toolkit から createSlice というライブラリをインポートして

```ts
const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0,
  },
  reducers: {
    increment: (state) => {
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload
    },
  },
})
```

createSlice を使って counterSlice というコンポーネントを作る
公式では export しているが、これを直接外部ファイルで使うことはない
なので export は必要ない。

中に名前、初期値、reducers を作る。
名前には counter, 初期値には 0 を入れて

reducers には increment, decrement, incrementByAmount を作る
increment は state を受け取って中の value を +1 するだけ
decrement は -1 同様にするだけ
incrementByAmount は state だけでなく action も受け取る。
そして state の中の value に action のなかの payload を加算する。

```ts
export const { increment, decrement, incrementByAmount } = counterSlice.actions

export default counterSlice.reducer
```

そしてこれらの reducers 一つ一つに counterSlice の actions を入れる。これがないと

> export 'increment' (imported as 'increment') was not found in './counterSlice' (possible exports: counterSlice, default

counterSlice から increment, decrement が読み取れないので必須。


これで counterSlice という state の　value を変化させる slice
その中の increment, decrement, incrementByAmount, の reducer
これらが　export できた。

---

## app/store で counterSlice から counterReducer を読み込む

このままでは React に導入した Store と、先ほど作った Slice が接続されていない。なので結びつける。

最初に書いた app/store.js に

```ts
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'

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

先ほど作った counterSlice をインポートして
空だった reducer の欄に coutnerSlicer を追加する。

これで store を redux が空で導入した時に出た

```ts
redux.js:426 Store does not have a valid reducer. 
Make sure the argument passed to combineReducers is 
an object whose values are reducers.
warning @ redux.js:426
```

reducer が何もないという警告は消えた。

---

## feature/counter/Counter.tsx で useSelector, useDispatch で slice と reducers を使う描画コンポーネントを作る

tsx じゃないと jsx の html っぽいものを使えない。

feature/counter/Counter.tsx に

https://react-redux.js.org/tutorials/quick-start#use-redux-state-and-actions-in-react-components

これらを App で使うためのコンポーネントを書く。

counterSlice によってグローバルに定義された counter
これを useSelector によって取ってきて
increment, decrement, incrementByAmount の reducers
これを インポートしてきて、dispatch によって動かせるようにする。

Counter.tsx に

```ts
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'
import styles from './Counter.module.css'

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

useSelector と useDispatch
increment と decrement
これらを持ってきて
持ってくるるのと発火せるロジックを作り

```ts
  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>
  )
```

ボタンで increment, decrement を dispatch させ
count を select で表示させる


---

## App で import する

```ts
import { Counter } from './features/counter/Counter';
...
        <img src={logo} className="App-logo" alt="logo" />
        <Counter />
```

Counter コンポーネントをインポートして
画像の下にレンダーされるようにする



---

## 動作確認

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/77exfcin2ilqujpgblec.png)

ブラウザで動くのを確認した。

---

## まとめ

index.js に Provider で App のルートを括り、store を繋げる

ストアファイルを作って、configureStore として reducer たちを入れるところを作る

createSlice を使って　counterSlice というコンポーネントを作り
name でグローバルステートの名前を counter と決めて
reducers に state の操作用に increment, decrement を作る

ストアファイルに counter を登録する

Counter というページコンポーネントを作って
useSelector でグローバルステートの counter を呼び、
useDispatch で reducer である increment, decrement を呼び
counter を表示し、ボタンで increment, decrement を使うロジックと UI を書く。

これで react-redux と redux toolkit を使って
counter の値を increment/decrement するアプリを作れた。

---

## 今後

使われていなかった incrementByAmount を CounterSlice で使えるようにし、新しく incrementAsnync も作り、これも使えるようにする。

このチュートリアルの現バージョンのドキュメントでは
incrementByAmount を使っていない。サンドボックスを見ると導入コードがあり、そこには incrementAsync というゆっくり反映されるボタンもあったので、ついでに作ってみる。