React Redux Tutorial Part 1 -- react-redux の導入 と reduxjs/toolkit の createSlice を使った counter アプリの作成
kaede
Posted on April 27, 2022
why
- 次のプロジェクトで必要とされるから。
- 以前のプロジェクトで携わったが、reducer や useSelector などを理解できてないままなんとなくで使ってしまっていたから。
- カスタムフックを作れるようになりたいから。
何をやるのか
https://react-redux.js.org/tutorials/quick-start
React-Redux の公式チュートリアル、クイックスタート
React アプリに redux toolkit の slice を導入して
グローバルステートの count の値をボタンで上下させるプロジェクトを作る。
プロジェクト作成とライブラリのインストール
npx create-react-app redux
これで React のプロジェクトディレクトリを作成
https://react-redux.js.org/tutorials/quick-start
この react-redux 公式チュートリアル通りに
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
にストアファイルを作ってみる
import { configureStore } from '@reduxjs/toolkit'
export default configureStore({
reducer: {},
})
toolkit のライブラリから configureStore というものをインポートする
configureStore の名前で export する。
中身の reducer はまだない。
普通の redux の combine のようなものだと推測する。
index.js で store ファイルをインポートして Provider に繋げる。
import store from './app/store'
import { Provider } from 'react-redux'
store ファイルをインポートして
react-redux のライブラリから Provider をインポートする
<React.StrictMode>
<App />
</React.StrictMode>
デフォルトでは StrictMode で App が括られているが
<Provider store={store}>
<App />
</Provider>
今回は Provider で括るようにする。
npm start で起動する
この store と Provider を入れた React アプリを起動すると普通に動く。
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 というファイルを作る
import { createSlice } from '@reduxjs/toolkit'
redux toolkit から createSlice というライブラリをインポートして
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 を加算する。
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 に
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
export default configureStore({
reducer: {
counter: counterReducer,
},
})
先ほど作った counterSlice をインポートして
空だった reducer の欄に coutnerSlicer を追加する。
これで store を redux が空で導入した時に出た
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 に
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
これらを持ってきて
持ってくるるのと発火せるロジックを作り
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 する
import { Counter } from './features/counter/Counter';
...
<img src={logo} className="App-logo" alt="logo" />
<Counter />
Counter コンポーネントをインポートして
画像の下にレンダーされるようにする
動作確認
ブラウザで動くのを確認した。
まとめ
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 というゆっくり反映されるボタンもあったので、ついでに作ってみる。
Posted on April 27, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.