# Redux Toolkit详细示例
# 关于 Redux Toolkit
官方推荐的标准的 Redux 逻辑开发模式
Redux Toolkit 包含:
configureStore()
(opens new window):创建一个顶层仓库,其封装了createStore
,简化配置项。import { configureStore } from '@reduxjs/toolkit' import usersReducer from '../features/users/usersSlice' import postsReducer from '../features/posts/postsSlice' export default configureStore({ reducer: { users: usersReducer, posts: postsReducer, } })
1
2
3
4
5
6
7
8
9
10
11createReducer()
(opens new window) 将 action type 映射到 reducer 函数,而不是编写 switch...case 语句。另外,它带有 proxy 更新,例如state.todos[3].completed = true
,并且它会自动调用。createAction()
(opens new window) 生成给定 action type 字符串的 action creator 函数。该函数本身已定义了toString()
,因此可以代替常量类型使用。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 }
1
2
3
4
5
6createSlice()
(opens new window) 创建初始化状态 initial state,并自动生成 reducer。createAsyncThunk
(opens new window): 接收一个 action type 字符串和一个返回值为 promise 的函数, 并生成一个 thunk 函数,这个 thunk 函数可以基于之前那个 promise ,dispatch 一组 type 为pending/fulfilled/rejected
的 action。createEntityAdapter
(opens new window): 生成一系列可复用的 reducer 和 selector,从而管理 store 中的规范化数据。createSelector
(opens new window) 来源于 Reselect (opens new window) 库,(记忆化数据) 并重新 export 出来以方便使用。
# 具体示例
src/features/todos/todosSlice.js
import {
createSlice,
createSelector,
createAsyncThunk,
createEntityAdapter
} from '@reduxjs/toolkit'
import { client } from '../../api/client'
import { StatusFilters } from '../filters/filtersSlice'
const todosAdapter = createEntityAdapter()
const initialState = todosAdapter.getInitialState({
status: 'idle'
})
// Thunk 函数
export const fetchTodos = createAsyncThunk('todos/fetchTodos', async () => {
const response = await client.get('/fakeApi/todos')
return response.todos
})
export const saveNewTodo = createAsyncThunk('todos/saveNewTodo', async text => {
const initialTodo = { text }
const response = await client.post('/fakeApi/todos', { todo: initialTodo })
return response.todo
})
const todosSlice = createSlice({
name: 'todos',
initialState,
reducers: {
todoToggled(state, action) {
const todoId = action.payload
const todo = state.entities[todoId]
todo.completed = !todo.completed
},
todoColorSelected: {
reducer(state, action) {
const { color, todoId } = action.payload
state.entities[todoId].color = color
},
prepare(todoId, color) {
return {
payload: { todoId, color }
}
}
},
todoDeleted: todosAdapter.removeOne,
allTodosCompleted(state, action) {
Object.values(state.entities).forEach(todo => {
todo.completed = true
})
},
completedTodosCleared(state, action) {
const completedIds = Object.values(state.entities)
.filter(todo => todo.completed)
.map(todo => todo.id)
todosAdapter.removeMany(state, completedIds)
}
},
extraReducers: builder => {
builder
.addCase(fetchTodos.pending, (state, action) => {
state.status = 'loading'
})
.addCase(fetchTodos.fulfilled, (state, action) => {
todosAdapter.setAll(state, action.payload)
state.status = 'idle'
})
.addCase(saveNewTodo.fulfilled, todosAdapter.addOne)
}
})
export const {
allTodosCompleted,
completedTodosCleared,
todoAdded,
todoColorSelected,
todoDeleted,
todoToggled
} = todosSlice.actions
export default todosSlice.reducer
export const { selectAll: selectTodos, selectById: selectTodoById } =
todosAdapter.getSelectors(state => state.todos)
export const selectTodoIds = createSelector(
// 首先,传递一个或多个 input selector 函数:
selectTodos,
// 然后,一个 output selector 接收所有输入结果作为参数
// 并返回最终结果
todos => todos.map(todo => todo.id)
)
export const selectFilteredTodos = createSelector(
// 第一个 input selector:所有 todos
selectTodos,
// 第二个 input selector:所有 filter 值
state => state.filters,
// Output selector: 接收两个值
(todos, filters) => {
const { status, colors } = filters
const showAllCompletions = status === StatusFilters.All
if (showAllCompletions && colors.length === 0) {
return todos
}
const completedStatus = status === StatusFilters.Completed
// 根据 filter 条件返回未完成或已完成的 todos
return todos.filter(todo => {
const statusMatches =
showAllCompletions || todo.completed === completedStatus
const colorMatches = colors.length === 0 || colors.includes(todo.color)
return statusMatches && colorMatches
})
}
)
export const selectFilteredTodoIds = createSelector(
// 传入记忆化 selector
selectFilteredTodos,
// 并在 output selector 中导出数据
filteredTodos => filteredTodos.map(todo => todo.id)
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
总结
- Redux Toolkit (RTK) 是编写 Redux 逻辑的标准方式
- RTK 包含用于简化大多数 Redux 代码的 API
- RTK 围绕 Redux 核心,并包含其他有用的包
configureStore
用来设置一个具有良好默认值的 Redux store- 自动组合 slice reducers 来创建根 reducer
- 自动设置 Redux DevTools 扩展和调试 middleware
createSlice
简化了 Redux actions 和 reducers 的编写- 根据 slice/reducer 名称自动生成 action creators
- Reducers 可以使用 Immer 在
createSlice
中“改变”(mutate)state
createAsyncThunk
为异步调用生成 thunk- 自动生成一个 thunk +
pending/fulfilled/rejected
action creators - dispatch thunk 运行 payload creator 并 dispatch actions
- 可以在
createSlice.extraReducers
中处理 thunk actions
- 自动生成一个 thunk +
createEntityAdapter
为标准化 state 提供了 reducers + selectors- 包括用于常见任务的 reducer 功能,例如添加/更新/删除 items
- 为
selectAll
和selectById
生成记忆化 selectors