Redux 的工作流程是什么?Redux Toolkit 有什么改进?

Redux 三大原则

  1. 单一数据源:整个应用只有一个 store
  2. 状态只读:只能通过 dispatch action 修改
  3. 纯函数修改: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

同分类其他题目