React 状态管理实战:用 Redux Toolkit 把全局状态写短写稳

test12026-10-182 次阅读

为什么还要用 Redux Toolkit

Zustand、Jotai 这类轻量方案适合中小项目,但当状态跨多个业务域、需要可预测的时间旅行调试、或者团队已经有 Redux 习惯时,Redux Toolkit(RTK)仍然是更省心的选择。RTK 用 createSlice 把 action、reducer、初始状态收进一处,用 createAsyncThunk 把异步流程标准化,reducer 内部允许直接"修改"草稿状态(底层用 Immer),比手写 switch-case 干净太多。

定义切片与异步 thunk

下面用一个文章列表的缓存切片举例,覆盖同步 reducer 与异步加载两种场景。

// store/articlesSlice.ts
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { fetchArticles } from '../api';

export const loadArticles = createAsyncThunk(
  'articles/load',
  async (page: number) => {
    const data = await fetchArticles(page);
    return data; // 返回值会进入 fulfilled 的 action.payload
  }
);

interface State {
  list: { id: number; title: string }[];
  loading: boolean;
  page: number;
}

const articlesSlice = createSlice({
  name: 'articles',
  initialState: { list: [], loading: false, page: 1 } as State,
  reducers: {
    reset(state) {
      state.list = [];
      state.page = 1;
    },
    append(state, action: PayloadAction<{ id: number; title: string }>) {
      state.list.push(action.payload);
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(loadArticles.pending, (state) => { state.loading = true; })
      .addCase(loadArticles.fulfilled, (state, action) => {
        state.loading = false;
        state.list.push(...action.payload);
        state.page += 1;
      })
      .addCase(loadArticles.rejected, (state) => { state.loading = false; });
  },
});

export const { reset, append } = articlesSlice.actions;
export default articlesSlice.reducer;

在组件里读取并避免无谓重渲染

组件用 useSelector 取状态。默认比较是全等,返回新对象或新数组会触发重渲染。多个字段建议用 createSelector 做记忆化,或者只挑最小切片。

// List.tsx
import { useSelector, useDispatch } from 'react-redux';
import { loadArticles, reset } from './store/articlesSlice';

export function ArticleList() {
  const dispatch = useDispatch();
  // 只取 loading 这一个原始值,变化频率低,不会引起多余渲染
  const loading = useSelector((s: RootState) => s.articles.loading);
  const list = useSelector((s: RootState) => s.articles.list);

  return (
    <div>
      <button onClick={() => dispatch(loadArticles(1))}>加载</button>
      {loading && <span>加载中...</span>}
      <ul>{list.map((it) => <li key={it.id}>{it.title}</li>)}</ul>
    </div>
  );
}

几个落地经验

  • 把异步 thunk 放在 slice 文件外或单独文件,保持 reducer 纯粹、可测试。
  • 列表类数据优先用实体适配器 createEntityAdapter,自带 selectById、selectAll 等记忆化选择器。
  • 调试时开 Redux DevTools,时间旅行能直接回放 thunk 的 pending/fulfilled/rejected 全过程。
  • 能用局部状态解决的就不要进全局 store,Redux 适合放真正跨页面共享的数据。
T

test1

文章作者

为什么还要用 Redux Toolkit Zustand、Jotai 这类轻量方案适合中小项目,但当状态跨多个业务域、...

分类
技术
发布时间
2026-10-18
字数
约 2572 字
阅读
2 次