React Redux Tutorial Part 2 -- counter アプリの incrementByAMount と incrementAsync を作成
Tags: react, redux
Published: 2022-04-30 09:27:25


## why

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

react-redux tutorial で counter アプリを作って
increment/decrement は実装して UI に組み込んだが
incrementByAmount は実装の途中で終わっていたので
その続きを codesandbox のリンクから追って進める。

# incrementByAmount を実際に使えるようにする

https://codesandbox.io/s/github/reduxjs/redux-essentials-counter-example/tree/master/?from-embed=&file=/src/features/counter/Counter.js:359-423

Counter.js で

```ts
import React, { useState } from 'react';
import { decrement, increment, incrementByAmount } from './counterSlice'
```

incrementByAmount で使う増加変数のために useState をインポート
incrementByAmount の reducer 本体をインポート

```ts
  const [incrementAmount, setIncrementAmount] = useState('2');
```

増加量を useState を使って 2 で初期化

```ts
        <input
          aria-label="Set increment amount"
          value={incrementAmount}
          onChange={e => setIncrementAmount(e.target.value)}
        />
```

増加量を変更できるロジックと UI を書き

```ts
        <button
          onClick={() =>
            dispatch(incrementByAmount(Number(incrementAmount) || 0))
          }
        >
          Add Amount
        </button>
```

クリックしたら増加量である incremenAmount の数だけ
incrementByAmount が動き
incrementAmount に Number() を当てた値が 0 であれば増加量が 0 になるエラーハンドリングも当てる

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

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

これで、 incrementByAmount は実装できた。

---
---

# incrementAsync を実装する

同じく codesandbox を参照。

CounterSlice.js の

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

export const incrementAsync = (amount) => (dispatch) => {
  setTimeout(() => {
    dispatch(incrementByAmount(amount))
  }, 1000)
}
```

increment, decrement, incrementByAmount
これらを export した後に

別で incrementAsnyc を定義して export する。
amount の他に、dispatch まで引数から受け取って
中で setTimeout を使い、incrementByAmount を dispatch している。

なぜ外部に書いているかは不明。

これを Counter.js で

```ts
import { decrement, increment, incrementByAmount, incrementAsync } from './counterSlice'

<button
  onClick={() => dispatch(incrementAsync(Number(incrementAmount) || 0))}
>
  Add Async
</button>
```

incrementAsnync を追加で読み込み、
incrementByAmount と同様に incrementAmount を数値時にしてエラー処理して incrementAsync を動かすボタンを作る

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9vno2a8360e6lvlrydya.png)
 
これで、押してから 1 秒後に反映される加算ボタンができた。

---

## incrementAsync を reducers の中身に作って失敗した

counterSlide の外部にわざわざ新しく incrementAsync を作って、そこで dispatch して counterSlide の中の incrementByAmount 作るのは無駄に思えた。

なので　reducers の内部から incrementbyAmount を呼ぶ
incrementAsyncInside を作ってみた。

```ts
  reducers: {
    incrementByAmount: (state, action) => {
      console.log(`increment BY Async worked`);
      state.value += action.payload
    },
    incrementAsyncInside: (state, action) => {
      setTimeout(() => {
        incrementByAmount(state, action)
      }, 1000)
    }
```

state と action をそのまま渡した。
そして export/import してボタンを作ってセットしたが、
こっちは動かなかった。
この console.log のメッセージも表示されない。
同じ reducers の一つから別の reducer は呼び出せないと推測する。

---

## まとめ

incrementByAmount は incrementAmount のローカルステートを作成し
その値を引数に渡して呼び出すだけなので楽だった。

incrementAsyncInside は、incrementByAmount を利用するので
reducers の外から dispatch で呼び出す必要があった。