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)