在這篇文章中,我們要來認識一個函式庫: Redux Toolkit。
不知道讀者有沒有感覺到寫 Redux 寫久了,像 Actions、Reducers 那些函式很像模板,就是格式都很相似,而且重複性有點高,然後我們常常會拆分成 actions、reducers、action types 各自為一個檔案。
而 Redux Toolkit 的出現可以幫助你更有效率撰寫 Redux 的一個 library,它提供了一些 API 讓你更方便的建立 Store、Actions 和 Reducers 並減少重複性質的程式碼。
以下介紹幾個 API 的作用,在下篇範例中我們將會實際使用它們其中幾個。
功用和 createStore 一樣可以建立 Store,但還可以結合 reducers、middleware。
範例:
import { configureStore } from '@reduxjs/toolkit'
import rootReducer from './reducers'
const store = configureStore({ reducer: rootReducer })
建立 action creator 的函式。放在 createAction() 裡面的參數會自動變成 action type,如範例中的 counter/increment
,在產生 increment 這個 action creator 之後,將參數帶進這個 action creator 會變成 action 的 payload。
範例:
import { createAction } from '@reduxjs/toolkit'
const increment = createAction('counter/increment')
let action = increment()
// { type: 'counter/increment' }
action = increment(3)
// returns { type: 'counter/increment', payload: 3 }
使用它在撰寫 reducer 的時候可以不用再用 switch case 語法,並且它的語法底層加入了 immer,因此可以使用會有 side effect 的寫法去變更 state,它背後會再幫你轉成 「immutable」的方式。
範例:
import { createAction, createReducer } from '@reduxjs/toolkit'
const increment = createAction('counter/increment')
const decrement = createAction('counter/decrement')
const incrementByAmount = createAction('counter/incrementByAmount')
const initialState = { value: 0 }
const counterReducer = createReducer(initialState, (builder) => {
builder
.addCase(increment, (state, action) => {
state.value++
})
.addCase(decrement, (state, action) => {
state.value--
})
.addCase(incrementByAmount, (state, action) => {
state.value += action.payload
})
})
將一個 slice 的 name、初始化的 state、reducer、action 統一在一個地方建立,並會產生 action creators 和 action type。
import { createSlice } from '@reduxjs/toolkit'
const initialState = { value: 0 }
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment(state) {
state.value++
},
decrement(state) {
state.value--
},
incrementByAmount(state, action) {
state.value += action.payload
},
},
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
用來處理非同步,會接受一個 action type 和一個回傳 promise 的 callback function,最後回傳一個 thunk action creator。
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { userAPI } from './userAPI'
// First, create the thunk
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (userId, thunkAPI) => {
const response = await userAPI.fetchById(userId)
return response.data
}
)
// Then, handle actions in your reducers:
const usersSlice = createSlice({
name: 'users',
initialState: { entities: [], loading: 'idle' },
reducers: {
// standard reducer logic, with auto-generated action types per reducer
},
extraReducers: (builder) => {
// Add reducers for additional action types here, and handle loading state as needed
builder.addCase(fetchUserById.fulfilled, (state, action) => {
// Add user to the state array
state.entities.push(action.payload)
})
},
})
// Later, dispatch the thunk as needed in the app
dispatch(fetchUserById(123))
下一篇將會針對原本用 Redux 寫的程式碼範例用 Redux Toolkit 去做改寫。
可以參考官網提供的以下範例,使用 [slice 名稱].caseReducers.[reducer 名稱]
即可使用指定的 reducer。
const slice = createSlice({
name: 'test',
initialState: 0,
reducers: {
increment: (state, action: PayloadAction<number>) => state + action.payload,
},
})
// also available:
slice.caseReducers.increment(0, { type: 'increment', payload: 5 })
有在持續更新的文章,必須推爆
咦?請問你怎麼知道的
難道是更新文章的話我的追蹤者們都會收到通知嗎XD
沒有拉,我最近面試被問到Redux Toolkit,想說來了解一下
哈哈好,希望對你有幫助~