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 というゆっくり反映されるボタンもあったので、ついでに作ってみる。
Top comments (0)