YSNHatenaBlog

主にアプリやWebサービス開発について

Redux Toolkitがすごい便利だった

画面遷移しても状態保存しておこうと思い、再度reduxを見直していたが、これを見つけてちょっと感動した。

redux-toolkit.js.org

reduxの構成にしたい人はこれ入れておくとよさそう。

action, reducer, selectorの記述が少ない

reduxは何と言ってもコードの記述量が増えるのがめんどくさいのだが、とてもスマートに書ける。

import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { CourtDoc } from 'models/court'
import type { RootState } from 'store'

type Mode = 'text' | 'location'
type Geo = {
  lat: number
  lng: number
}

interface HomeState {
  mode: Mode
  zoom: number
  center: Geo
  courts?: CourtDoc[]
}

const initialState: HomeState = {
  mode: 'text',
  zoom: 11,
  center: { lat: 35.681236, lng: 139.767125 },
}

export const homeSlice = createSlice({
  name: 'home',
  initialState,
  reducers: {
    changeMode: (state, action: PayloadAction<Mode>) => {
      state.mode = action.payload
    },
    changeZoom: (state, action: PayloadAction<number>) => {
      state.zoom = action.payload
    },
    changeCenter: (state, action: PayloadAction<Geo>) => {
      state.center = action.payload
    },
  },
})

export const { changeMode, changeZoom, changeCenter } = homeSlice.actions

export const selectMode = (state: RootState): Mode => state.home.mode
export const selectZoom = (state: RootState): number => state.home.zoom
export const selectCenter = (state: RootState): Geo => state.home.center
export const selectCourtDocs = (state: RootState): CourtDoc[] | undefined =>
  state.home.courts

export default homeSlice.reducer

actionのstringを自分で書かなくても、 createSlicenameと関数名から自動でいい感じに生成してくれる。 reducers の書き換えをしてもglobalに影響が出ないようになっている。

reselect

まだやってないけど createSelector でSelectorを作ると reselect が使われてパフォーマンス上がる模様。 createSelector | Redux Toolkit

TypeScript対応

型がちゃんと付く。

フラグ一個でredux-devtools-extensionがつく

export const store = configureStore({
  reducer: {
    firebase: firebaseReducer,
    firestore: firestoreReducer,
    home: HomeReducer,
  },
  devTools: true, // <- これだけ
})

副作用の対応

前述のcreateSliceのところで、

import { createAsyncThunk, ... } from '@reduxjs/toolkit'
import { search } from 'models/search'
...

export const searchByText = createAsyncThunk(
  'home/searchByText',
  async (params: { text: string; hits: number }) => {
    const courtDocs = await search(params.text, params.hits)
    return courtDocs
  }
)

export const homeSlice = createSlice({
  ...,
  extraReducers: {
    [searchByText.fulfilled.type]: (
      state,
      action: PayloadAction<CourtDoc[]>
    ) => {
      state.courts = action.payload
    },
  },
})

...
export default homeSlice.reducer

これでいい。

すごい便利だ...。まだかじっただけなので、もうちょっと調べる。