本文档解释了如何使用 Redux Toolkit 的 createSlice API,通过集成 Immer 来简化 reducer 和 action 的创建。createSlice 是 Redux Toolkit 中的一个核心函数,它通过自动生成 action creators 和 action types,并借助 Immer 实现更简单的 reducer 逻辑,从而帮助消除 Redux 样板代码。
有关 Redux 异步操作的信息,请参阅使用 createAsyncThunk 的异步逻辑。有关 store 配置,请参阅configureStore。
createSlice 是一个高阶函数,它接受一个初始状态、一个 reducer 函数对象和一个 slice 名称,并自动生成与 reducer 和状态对应的 action creators 和 action types。
Sources: docs/tutorials/essentials/part-2-app-structure.md235-283
createSlice 显著减少了您需要编写的 Redux 样板代码。您无需手动单独定义 action types、action creators 和 reducers,createSlice 会为您处理所有这些。
下图展示了 createSlice 如何将传统的 Redux 模式转换为更简单的代码
Sources: docs/tutorials/essentials/part-2-app-structure.md238-297 docs/tutorials/essentials/part-4-using-data.md372-422
使用 createSlice 的基本模式是
这是一个如何定义 slice reducer 的简单示例
// Define slice with createSlice
const counterSlice = createSlice({
name: 'counter',
initialState: 0,
reducers: {
increment: state => state + 1,
decrement: state => state - 1,
incrementByAmount: (state, action) => state + action.payload
}
})
// Extract generated action creators
export const { increment, decrement, incrementByAmount } = counterSlice.actions
// Export the reducer function for the store
export default counterSlice.reducer
Sources: docs/tutorials/essentials/part-2-app-structure.md240-284 docs/tutorials/essentials/part-3-data-flow.md273-299
createSlice 最强大的功能之一是它与 Immer 库的集成,这使您能够编写看起来“改变”状态但实际上产生不可变更新的 reducers。
function handwrittenReducer(state, action) {
return {
...state,
first: {
...state.first,
second: {
...state.first.second,
[action.someId]: {
...state.first.second[action.someId],
fourth: action.someValue
}
}
}
}
}
const sliceWithImmer = createSlice({
name: 'test',
initialState: { first: { second: { someId: { fourth: 'value' } } } },
reducers: {
someAction(state, action) {
// This looks like mutation, but Immer makes it safe!
state.first.second[action.payload.someId].fourth = action.payload.someValue
}
}
})
Immer 的工作原理是利用 JavaScript 的 Proxy 特性来包装您的状态并跟踪所有尝试修改它的操作。当 reducer 函数完成时,Immer 会获取所有检测到的更改,并生成一个反映这些更改的新的不可变状态对象。
Sources: docs/tutorials/essentials/part-2-app-structure.md329-383 docs/tutorials/essentials/part-4-using-data.md345-372
在 reducers 对象中,每个键都成为一个 action type 字符串,每个函数都是处理该 action 的 reducer。
const postsSlice = createSlice({
name: 'posts',
initialState: [],
reducers: {
// Basic reducer: just one argument (state)
allPostsLoaded(state) {
// do something with state
},
// Reducer with payload: (state, action)
postAdded(state, action) {
state.push(action.payload)
}
}
})
Sources: docs/tutorials/essentials/part-3-data-flow.md273-299 docs/tutorials/essentials/part-4-using-data.md216-247
prepare 回调有时,您需要在 action payload 到达 reducer 之前进行额外的准备工作。prepare 回调允许您自定义 action creator
示例
const postsSlice = createSlice({
name: 'posts',
initialState: [],
reducers: {
// Object notation with reducer and prepare
postAdded: {
reducer(state, action) {
state.push(action.payload)
},
prepare(title, content, userId) {
// Do work to prepare the payload
return {
payload: {
id: nanoid(),
date: new Date().toISOString(),
title,
content,
user: userId
}
}
}
}
}
})
Sources: docs/tutorials/essentials/part-4-using-data.md370-442
虽然 reducers 处理此 slice 创建的 action,但 extraReducers 允许此 slice 响应其他 action,包括由其他 slice 或异步 thunk 派发的 action。
有两种方式定义 extraReducers
const postsSlice = createSlice({
name: 'posts',
initialState,
reducers: {
// ...reducer methods
},
extraReducers: builder => {
builder
.addCase(fetchPosts.pending, (state, action) => {
state.status = 'loading'
})
.addCase(fetchPosts.fulfilled, (state, action) => {
state.status = 'succeeded'
// Add fetched posts to the array
state.posts.push(...action.payload)
})
.addCase(fetchPosts.rejected, (state, action) => {
state.status = 'failed'
state.error = action.error.message
})
}
})
推荐使用 builder callback 模式,因为它提供了完整的 TypeScript 类型安全。
Sources: docs/tutorials/essentials/part-5-async-logic.md478-516 docs/tutorials/essentials/part-6-performance-normalization.md600-675
createSlice 对 TypeScript 有很好的支持。以下是如何将其与 TypeScript 一起使用
// Define a TypeScript interface for your state
interface CounterState {
value: number
status: 'idle' | 'loading' | 'failed'
}
// Use the interface with initialState
const initialState: CounterState = {
value: 0,
status: 'idle'
}
// createSlice with typed state
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
// TypeScript will infer the argument types and return type
increment(state) {
state.value += 1
},
// Use PayloadAction for actions with a payload
incrementByAmount(state, action: PayloadAction<number>) {
state.value += action.payload
}
}
})
Sources: docs/tutorials/essentials/part-2-app-structure.md237-284 docs/usage/UsageWithTypescript.md129-143
某些版本的 Redux Toolkit 支持直接在 createSlice 中定义选择器
const postsSlice = createSlice({
name: 'posts',
initialState,
reducers: {
// reducer functions
},
// Optional selectors field
selectors: {
// Selectors receive the slice state
selectAllPosts: postsState => postsState,
selectPostById: (postsState, postId) => {
return postsState.find(post => post.id === postId)
}
}
})
// Can export selectors from the slice
export const { selectAllPosts, selectPostById } = postsSlice.selectors
Sources: docs/tutorials/essentials/part-4-using-data.md539-589
高级用法允许使用 reducers 的回调语法在 createSlice 中定义 thunk
const createAppSlice = buildCreateSlice({
creators: { asyncThunk: asyncThunkCreator }
})
const postsSlice = createAppSlice({
name: 'posts',
initialState,
reducers: create => {
return {
// Define async thunk within the slice
fetchPosts: create.asyncThunk(
async () => {
const response = await client.get('/posts')
return response.data
},
{
pending: (state) => {
state.status = 'loading'
},
fulfilled: (state, action) => {
state.status = 'succeeded'
state.posts.push(...action.payload)
},
rejected: (state, action) => {
state.status = 'failed'
state.error = action.error.message
}
}
)
}
}
})
Sources: docs/tutorials/essentials/part-5-async-logic.md736-834
每个特性一个 Slice:为应用程序的每个特性或领域区域组织 Redux 状态,每个特性一个 slice。
Slice 文件结构:将 slice 的所有逻辑放入单个文件中
使用 Immer 进行不可变更新:利用 Immer 的“类修改”语法来编写更简洁的代码。
使用 Prepare 回调:对于复杂的 action payload,使用 prepare 回调模式来封装 action 创建逻辑。
extraReducers 使用 Builder Callback:为了更好的 TypeScript 类型安全,extraReducers 请使用 builder callback 模式。
导出类型化的 Hooks:创建并使用包含您的 Redux store 类型的预类型化 Hooks。
// app/hooks.ts
import { useDispatch, useSelector } from 'react-redux'
import type { AppDispatch, RootState } from './store'
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
export const useAppSelector = useSelector.withTypes<RootState>()
Sources: examples/counter-ts/src/app/hooks.ts1-7 docs/usage/UsageWithTypescript.md76-117
createSlice 是一个强大的 Redux Toolkit 函数,它通过以下方式简化了 Redux 开发:
通过使用 createSlice,您可以显著减少样板代码,使您的 Redux 逻辑更易于维护,并专注于您的业务逻辑而非 Redux 实现细节。
刷新此 Wiki
最后索引时间2025 年 4 月 18 日 (304031)