LoginSignup
86
85

More than 3 years have passed since last update.

React Reduxには今後Redux Toolkitも使うのがいいと思う

Last updated at Posted at 2020-03-30

ReactをTypeScriptで始めることが随分楽になったと感じています。Create React Appの以下のコマンドでOKです。

$ npx create-react-app my-app --template typescript

2020年2月の中頃からReduxのテンプレートが利用可能となり、以下のコマンドで始めることができます。

$ npx create-react-app my-app --template redux
# or 
$ npx create-react-app my-app --template redux-typescript

このReduxテンプレートにはRedux Toolkitが使われています。https://redux-toolkit.js.org/
本格的に利用していくのはこれからですが、使わない場合と比べてここが便利だ(そうだ)というあたりを紹介します。

利点

初期設定が簡単

次のmiddlewareが設定済みとなります。

  • Redux Thunk
  • immutable-state-invariant
  • serializable-state-invariant-middleware

Redux Thunkは非同期通信のためのライブラリで、他2つは開発用のmiddlewareでReduxで禁止しているstateの変更を検知してくれます。

また、Redux Devtools Extensionも有効になっています。
これらを自分で適用しようとすると少々面倒です。

createSliceでActionのコードは不要に

公式ドキュメントからの抜粋となります

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    increment: state => state + 1,
    decrement: state => state - 1
  }
})

const store = configureStore({
  reducer: counterSlice.reducer
})

const { actions, reducer } = counterSlice
const { increment, decrement } = actions

こんな感じでAction, Reducerを定義できます。これによりほぼActionのコードがなくなるのではと期待しています。今まではComponent, Action, Reducerに対してロジックが多少入ることが気になっていましたが、Component, Reducerでそれぞれの責務でロジックを実装できそうです :smiley:

Reducerのネストはいつも通りな感じです。

const sliceA = createSlice({
  name: "reducerA",
  //...
})
const sliceB = createSlice({
  name: "reducerB",
  //...
})

import { combineReducers } from "@reduxjs/toolkit"
const mergeReducer = combineReducers({
  reducerA: sliceA.reducer,
  reducerB: sliceB.reducer,
})

非同期通信にはRedux Thunkが採用


const createUser = createAsyncThunk(
  'users/createUser',
  async () => {
    // API処理
  }
)

createAsyncThunkでは次の3つのActionを生成してくれます。

  • pending
  • fulfilled
  • rejected

sliceではextraReducersのbuilderを利用して追加します。createAsyncThunkで定義されたActionを利用するためです。

reducer.js
const userSlice = createSlice({
  name: "user",
  initialState: {
    user: null,
  },
  reducers: {},
  extraReducers: builder => {
    builder.addCase(createUser.pending, (state, action) => {})
    builder.addCase(createUser.fulfilled, (state, action) => {})
    builder.addCase(createUser.rejected, (state, action) => {})
  }
})

component.jsx
import { useDispatch } from "react-redux"

function UserComponent() {
  const dispatch = useDispatch()

  const handleSubmit = async () => {
    const resultAction = await dispatch(createUser())
    // createAsyncThunkで生成されるActionと比較して処理を変更できる
    if (createUser.fulfilled.match(resultAction)) {
      // success
    } else {
      // error
    }
  };
  return (
    <div>
      <form></form>
      <button onClick={handleSubmit}>submit</button>
    </div>
  )
}

やっておいた方がいいこと

RootReducerの型定義

TypeScriptで利用する際に用意しておいた方がいいです。以下で定義しておきます。

store.ts
import { combineReducers, configureStore } from "@reduxjs/toolkit"

export const rootReducer = combineReducers({
  moduleA: moduleAReducer,
})
export type RootReducerType = ReturnType<typeof RootReducer>

RootとなるReducerの型定義はあると便利ぐらいの気持ちです。

useDispatchのラップ

store.ts
import { configureStore } from "@reduxjs/toolkit"
const store = configureStore({
  reducer: rootReducer
})
export type AppDispatch = typeof store.dispatch
customHooks.ts
import { useDispatch } from "react-redux"
function useAppDispatch(): AppDispatch {
  return useDispatch<AppDispatch>()
}

createAsyncThunkで型定義のエラーを起こさないためです。

注意点

ImmerによるReducerのstateのImmutable

以下のようにstateを直接変更するようなコードを書いても問題ありません。Redux ToolkitではImmerを利用しており、実際にstateを変更しているわけではないとのことです。

interface CounterState {
  value: number;
}

const initialState: CounterState = {
  value: 0,
};

export const slice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: state => {
      state.value += 1;
    }
  },
});

まとめ

煩雑な設定がなくなり、ActionやReducerの実装も統一できそうです。これからReduxを始める人、そうでない人にもおすすめです :smiley:

参考

86
85
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
86
85