在 ReactConf 2017 上,Redux 的创造者 Dan Abramov 介绍了 Redux 应用架构的一些思考,本文将从这些思考出发,探讨 Redux 应用架构的优化和最佳实践。
Redux 应用架构的问题
在 Dan Abramov 的演讲中,他提到了 Redux 应用架构存在的一些问题:
- Action 和 Reducer 的代码量过大,难以维护。
- 异步操作的处理需要使用中间件,增加了代码复杂度。
- 大量的冗余代码,例如每个 action 都需要定义一个 type 常量。
这些问题都是 Redux 应用架构的痛点,我们需要找到一些方法来解决这些问题。
Redux Toolkit
Redux Toolkit 是 Redux 官方提供的一个工具集,旨在解决 Redux 应用架构中的一些问题。Redux Toolkit 提供了以下功能:
- createSlice 函数,可以帮助我们更容易地创建 Reducer。
- createAsyncThunk 函数,可以帮助我们更容易地处理异步操作。
- configureStore 函数,可以帮助我们更容易地创建 Store。
createSlice 函数
createSlice 函数可以帮助我们更容易地创建 Reducer。它使用了 Immer 库,可以让我们使用类似于直接修改对象的方式来修改 state。例如:
// www.javascriptcn.com code example
import { createSlice } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
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这个例子中,我们使用 createSlice 函数创建了一个名为 counter 的 Reducer,它有三个 action:increment、decrement 和 incrementByAmount。我们可以直接调用这些 action 来修改 state。
createAsyncThunk 函数
createAsyncThunk 函数可以帮助我们更容易地处理异步操作。它创建了一个带有 pending、fulfilled 和 rejected 三种状态的 action,可以让我们更容易地处理异步操作的结果。例如:
// www.javascriptcn.com code example
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { fetchCount } from './counterAPI'
export const incrementAsync = createAsyncThunk(
'counter/fetchCount',
async amount => {
const response = await fetchCount(amount)
return response.data
}
)
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0, status: 'idle' },
reducers: {
increment: state => {
state.value++
},
decrement: state => {
state.value--
},
incrementByAmount: (state, action) => {
state.value += action.payload
}
},
extraReducers: builder => {
builder
.addCase(incrementAsync.pending, state => {
state.status = 'loading'
})
.addCase(incrementAsync.fulfilled, (state, action) => {
state.status = 'succeeded'
state.value += action.payload
})
.addCase(incrementAsync.rejected, state => {
state.status = 'failed'
})
}
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer这个例子中,我们使用 createAsyncThunk 函数创建了一个名为 incrementAsync 的 action,它调用了一个异步函数 fetchCount。我们可以使用 builder.addCase 函数来处理这个 action 的三种状态:pending、fulfilled 和 rejected。
configureStore 函数
configureStore 函数可以帮助我们更容易地创建 Store。它使用了 Redux DevTools Extension,可以让我们更容易地调试 Redux 应用。例如:
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
export const store = configureStore({
reducer: {
counter: counterReducer
}
})这个例子中,我们使用 configureStore 函数创建了一个 Store,它包含了一个名为 counter 的 Reducer。
Redux 最佳实践
除了使用 Redux Toolkit,我们还需要遵循一些最佳实践来优化 Redux 应用架构:
- 将 state 分解为多个小的片段,每个片段都有自己的 Reducer。
- 避免在 Reducer 中进行复杂的计算,应该将这些计算放在 Selector 中。
- 尽量使用 Immutable Data,可以使用 Immutable.js 或者 Immer 库来实现。
- 使用 Redux DevTools Extension 来调试 Redux 应用。
示例代码
下面是一个使用 Redux Toolkit 和最佳实践的示例代码:
// www.javascriptcn.com code example
import { createSlice, createSelector } from '@reduxjs/toolkit'
import { fetchItems } from './api'
const itemsSlice = createSlice({
name: 'items',
initialState: { data: [], status: 'idle' },
reducers: {
addItem: (state, action) => {
state.data.push(action.payload)
},
removeItem: (state, action) => {
state.data = state.data.filter(item => item.id !== action.payload.id)
},
setItems: (state, action) => {
state.data = action.payload
},
setStatus: (state, action) => {
state.status = action.payload
}
}
})
export const { addItem, removeItem, setItems, setStatus } = itemsSlice.actions
export const fetchItemsAsync = () => async dispatch => {
dispatch(setStatus('loading'))
const response = await fetchItems()
dispatch(setItems(response.data))
dispatch(setStatus('succeeded'))
}
export default itemsSlice.reducer
export const selectItems = state => state.items.data
export const selectStatus = state => state.items.status
export const selectItemCount = createSelector(selectItems, items => items.length)
export const selectItemById = itemId =>
createSelector(selectItems, items => items.find(item => item.id === itemId))这个例子中,我们使用了 Redux Toolkit 的 createSlice 函数来创建了一个名为 items 的 Reducer,它有四个 action:addItem、removeItem、setItems 和 setStatus。我们还使用了 createAsyncThunk 函数来创建了一个名为 fetchItemsAsync 的异步 action。
我们还遵循了最佳实践,将 state 分解为多个小的片段,每个片段都有自己的 Reducer。我们避免在 Reducer 中进行复杂的计算,将这些计算放在 Selector 中。我们使用了 Immutable Data,使用了 Redux DevTools Extension 来调试 Redux 应用。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d0ea6da941bf71341f4747