
在 React 生态中,状态管理始终是核心议题。随着 Hooks 的普及,Redux 官方也推出了 Redux Toolkit (RTK),极大地简化了 Redux 的配置与使用。与此同时,Ant Design 作为企业级 UI 组件库,与 React 结合紧密。本文将深入探讨如何利用 React Hooks、Redux Toolkit 和 Ant Design 构建一个类型安全、高性能且易于维护的前端应用,涵盖从架构设计到性能优化的完整实践。
useReducer + useContext 虽能解决部分状态问题,但无法替代 Redux 在复杂应用中的全局状态管理与调试能力。三者结合,可以兼顾开发效率、运行性能和代码可维护性。
使用 Create React App 或 Vite 初始化项目,安装核心依赖:
# 使用 CRA
npx create-react-app my-app --template typescript
# 安装 Redux 相关
npm install @reduxjs/toolkit react-redux
# 安装 Ant Design
npm install antd
# 按需加载样式(使用 craco 或 customize-cra 配置,Vite 原生支持)若使用 Vite,可更便捷地配置 Ant Design 按需加载:
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true,
modifyVars: { '@primary-color': '#1890ff' },
},
},
},
});RTK 的核心是 createSlice 和 configureStore。
Slice 整合了 reducer 和 action,并自动生成 action creators:
// store/features/counter/counterSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface CounterState {
value: number;
status: 'idle' | 'loading' | 'failed';
}
const initialState: CounterState = {
value: 0,
status: 'idle',
};
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1; // 使用 Immer 直接修改
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;// store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './features/counter/counterSlice';
export const store = configureStore({
reducer: {
counter: counterReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './store';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(
<Provider store={store}>
<App />
</Provider>
);Hooks 时代的 Redux 使用方式更简洁:useSelector 获取状态,useDispatch 触发 action。
假设我们有一个用户信息表单,需要将表单数据同步到 Redux。
// features/user/UserForm.tsx
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Form, Input, Button } from 'antd';
import { RootState } from '../../store';
import { updateUserInfo } from './userSlice';
const UserForm: React.FC = () => {
const dispatch = useDispatch();
const userInfo = useSelector((state: RootState) => state.user.info);
const [form] = Form.useForm();
// 当 Redux 状态变化时,回填表单
useEffect(() => {
form.setFieldsValue(userInfo);
}, [userInfo, form]);
const onFinish = (values: any) => {
dispatch(updateUserInfo(values));
// 可配合 Ant Design message 提示保存成功
};
return (
<Form form={form} layout="vertical" onFinish={onFinish}>
<Form.Item name="name" label="姓名" rules={[{ required: true }]}>
<Input />
</Form.Item>
<Form.Item name="email" label="邮箱" rules={[{ type: 'email' }]}>
<Input />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
保存
</Button>
</Form.Item>
</Form>
);
};
export default UserForm;使用 useSelector 获取列表数据,配合 Table 组件渲染,并支持加载状态。
// features/product/ProductList.tsx
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Table, Spin, Button } from 'antd';
import { RootState } from '../../store';
import { fetchProducts } from './productSlice';
const columns = [
{ title: 'ID', dataIndex: 'id', key: 'id' },
{ title: '名称', dataIndex: 'name', key: 'name' },
{ title: '价格', dataIndex: 'price', key: 'price' },
];
const ProductList: React.FC = () => {
const dispatch = useDispatch();
const { items, loading, error } = useSelector((state: RootState) => state.product);
useEffect(() => {
dispatch(fetchProducts());
}, [dispatch]);
if (error) return <div>加载失败:{error}</div>;
return (
<Spin spinning={loading}>
<Table dataSource={items} columns={columns} rowKey="id" />
<Button onClick={() => dispatch(fetchProducts())}>刷新</Button>
</Spin>
);
};
export default ProductList;RTK 内置了 createAsyncThunk,用于处理异步操作,自动生成 pending、fulfilled、rejected 三种 action,我们可以在 extraReducers 中处理状态变更。
// features/product/productSlice.ts
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { fetchProductApi } from '../../api/product';
interface Product {
id: string;
name: string;
price: number;
}
interface ProductState {
items: Product[];
loading: boolean;
error: string | null;
}
const initialState: ProductState = {
items: [],
loading: false,
error: null,
};
export const fetchProducts = createAsyncThunk(
'product/fetchProducts',
async (_, { rejectWithValue }) => {
try {
const response = await fetchProductApi();
return response.data;
} catch (err: any) {
return rejectWithValue(err.message);
}
}
);
const productSlice = createSlice({
name: 'product',
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchProducts.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(fetchProducts.fulfilled, (state, action) => {
state.loading = false;
state.items = action.payload;
})
.addCase(fetchProducts.rejected, (state, action) => {
state.loading = false;
state.error = action.payload as string || '未知错误';
});
},
});
export default productSlice.reducer;随着应用增大,useSelector 每次执行都会比较返回结果,若返回新对象可能引发不必要的重渲染。推荐使用 createSelector 创建记忆化选择器。
// store/selectors/productSelectors.ts
import { createSelector } from '@reduxjs/toolkit';
import { RootState } from '..';
export const selectProductItems = (state: RootState) => state.product.items;
export const selectProductCount = createSelector(
[selectProductItems],
(items) => items.length
);
// 在组件中使用
const count = useSelector(selectProductCount);同时,在组件中合理使用 React.memo、useMemo、useCallback 避免子组件不必要的更新。
RTK 的 configureStore 默认集成了 thunk,我们也可以添加自定义中间件进行日志、错误捕获等。
// store/index.ts
import { configureStore, Middleware } from '@reduxjs/toolkit';
import logger from 'redux-logger';
const errorMiddleware: Middleware = (store) => (next) => (action) => {
try {
return next(action);
} catch (err) {
console.error('Redux action error:', err);
// 可触发全局错误提示,如 Ant Design message.error
return action;
}
};
export const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware()
.concat(logger) // 开发环境使用
.concat(errorMiddleware),
});推荐按功能模块划分,保持清晰:
src/
├── api/ # API 请求封装
├── store/
│ ├── index.ts # 配置 store
│ ├── rootReducer.ts # 合并 reducer
│ └── features/ # 按业务划分
│ ├── user/
│ │ ├── userSlice.ts
│ │ └── userSelectors.ts
│ └── product/
├── components/ # 通用 UI 组件
├── pages/ # 页面级组件
├── hooks/ # 自定义 Hooks
├── utils/ # 工具函数
└── App.tsx将 useDispatch 和 useSelector 封装为带类型的自定义 Hook,减少重复代码:
// hooks/useAppDispatch.ts
import { useDispatch, useSelector } from 'react-redux';
import type { TypedUseSelectorHook } from 'react-redux';
import type { RootState, AppDispatch } from '../store';
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;在组件中直接使用 useAppDispatch 和 useAppSelector,获得完整的类型推断。
通过 Redux Toolkit + React-Redux Hooks + Ant Design 的组合,我们能够以极低的样板代码成本构建出具备全局状态管理、异步数据流、类型安全、UI 组件丰富的大型应用。本文重点实践了:
useSelector / useDispatch 与 Ant Design 表单、表格的集成这套方案已在多个企业级项目中得到验证,既保持了 Redux 的强大调试能力,又兼顾了开发体验。未来还可以结合 RTK Query 进一步简化数据获取逻辑,甚至探索 Redux 与 Server Components 的协同,但核心思路依然围绕“关注点分离”与“可预测的状态更新”展开。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。