Redux 的工作流程是什么?Redux Toolkit 有什么改进?
Redux 三大原则
- 单一数据源:整个应用只有一个 store
- 状态只读:只能通过 dispatch action 修改
- 纯函数修改:reducer 必须是纯函数
工作流程
View dispatch(action) → Reducer(state, action) → New State → View 更新传统 Redux
// Action Types
const INCREMENT = 'INCREMENT'
const DECREMENT = 'DECREMENT'
// Action Creators
const increment = () => ({ type: INCREMENT })
const decrement = () => ({ type: DECREMENT })
// Reducer
function counter(state = { count: 0 }, action) {
switch (action.type) {
case INCREMENT: return { ...state, count: state.count + 1 }
case DECREMENT: return { ...state, count: state.count - 1 }
default: return state
}
}
// Store
const store = createStore(counter)
// 使用
store.dispatch(increment())Redux Toolkit(RTK)
RTK 是官方推荐的编写方式,大幅简化代码:
import { createSlice, configureStore } from '@reduxjs/toolkit'
// 一个 slice 包含 reducer + actions
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: {
// RTK 内部使用 Immer,可以直接"修改"状态
increment: (state) => { state.count += 1 },
decrement: (state) => { state.count -= 1 },
incrementBy: (state, action) => { state.count += action.payload }
}
})
// 自动生成 actions
export const { increment, decrement, incrementBy } = counterSlice.actions
// 配置 store
const store = configureStore({
reducer: { counter: counterSlice.reducer }
})React 中使用
import { useSelector, useDispatch } from 'react-redux'
function Counter() {
const count = useSelector(state => state.counter.count)
const dispatch = useDispatch()
return (
<div>
<span>{count}</span>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(incrementBy(5))}>+5</button>
</div>
)
}RTK 的改进
| 特性 | 传统 Redux | Redux Toolkit |
|---|---|---|
| 样板代码 | 多 | 少 |
| 不可变更新 | 手动展开 | Immer 自动处理 |
| Action Creator | 手动写 | 自动生成 |
| 异步处理 | redux-thunk/saga | createAsyncThunk |
| TypeScript | 需要大量类型 | 自动推断 |
createAsyncThunk(异步)
const fetchUser = createAsyncThunk('user/fetch', async (id) => {
const res = await api.getUser(id)
return res.data
})
const userSlice = createSlice({
name: 'user',
initialState: { data: null, status: 'idle' },
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => { state.status = 'loading' })
.addCase(fetchUser.fulfilled, (state, action) => {
state.status = 'succeeded'
state.data = action.payload
})
.addCase(fetchUser.rejected, (state) => { state.status = 'failed' })
}
})什么时候用 Redux
- 大型应用,状态复杂
- 需要时间旅行调试
- 多个不相关组件共享状态
- 中小型项目可以考虑 Zustand / Jotai 等更轻量的方案