Nick Wilde, you're under arrest for felony tax evasion.
捕个der,还好狐狸和兔子有生殖隔离,不然我要被气鼠。
本文系统逮捕 react-redux 的使用方法,包括最新的 Redux Toolkit、TypeScript 类型化、connect高阶组件和hooks用法,以及 React 18 特性相关注意事项。
一、安装与项目初始化
首先,创建一个 Vite + React 项目即可::
npm create vite@latest my-app --template react-ts
安装必要依赖
npm install @reduxjs/toolkit react-redux
二、创建 Redux Store
1. 基本 Store
在 src/store/index.ts 中创建 Redux store:
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
const store = configureStore({
reducer: {
counter: counterReducer,
},
})
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
export default store
Redux Toolkit 的
configureStore会自动开启 Redux DevTools,并默认包含 thunk 中间件。
2. 在 React 中提供 Store
在入口文件 src/main.tsx 或 src/index.tsx 中:
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import store from './app/store';
import App from './App';
createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
三、创建切片(Slice)和 Action
1. 使用 createSlice
在 src/features/counter/counterSlice.ts 中:
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import type { RootState } from '../../app/store';
// 定义状态类型
interface CounterState {
value: number;
}
// 初始状态
const initialState: CounterState = {
value: 0,
};
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;
},
},
});
// 导出 action
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
// Selector
export const selectCount = (state: RootState) => state.counter.value;
export default counterSlice.reducer;
createSlice内部使用Immer,可直接写“可变”逻辑,Redux 自动生成不可变更新。
四、在组件中使用 Redux
1. Hooks 用法(现代推荐)
创建自定义类型化 Hooks src/store/hooks.ts:
import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux';
import type { RootState, AppDispatch } from './store';
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
组件示例 src/features/counter/Counter.tsx:
import React from 'react';
import { useAppSelector, useAppDispatch } from 'app/hooks';
import { increment, decrement } from './counterSlice';
export function Counter() {
const count = useAppSelector((state) => state.counter.value);
const dispatch = useAppDispatch();
return (
<div>
<button onClick={() => dispatch(increment())}>+</button>
<span>{count}</span>
<button onClick={() => dispatch(decrement())}>-</button>
</div>
);
}
Hooks 用法简洁、现代,适合绝大多数新项目。
2. connect 高阶组件用法(老项目或 Class 组件)
import { connect, ConnectedProps } from 'react-redux';
import { increment, decrement, incrementByAmount } from './counterSlice';
import type { RootState } from '@/store';
const mapState = (state: RootState) => ({
value: state.counter.value,
});
const mapDispatch = {
increment,
decrement,
incrementByAmount,
};
const connector = connect(mapState, mapDispatch);
type PropsFromRedux = ConnectedProps<typeof connector>;
interface OwnProps { title?: string; }
type Props = PropsFromRedux & OwnProps;
const CounterConnect = (props: Props) => {
const { title, value, increment, decrement, incrementByAmount } = props;
return (
<div>
<h2>{title}</h2>
<p>{value}</p>
<button onClick={() => increment()}>+1</button>
<button onClick={() => decrement()}>-1</button>
<button onClick={() => incrementByAmount(5)}>+5</button>
</div>
);
};
export default connector(CounterConnect);
connect自动注入 state 和 action 为 props,并进行浅比较优化,适合性能敏感的大型组件。
五、Hooks 与 Connect 区别
| 特性 | Hooks (useSelector/useDispatch) | Connect 高阶组件 |
|---|---|---|
| 写法 | 简单、现代 | 冗长,但清晰 |
| TS 类型 | 需自定义 Hooks | ConnectedProps 自动推断 |
| 性能 | 可能无意中多次渲染,需要 memo 或 selector 优化 | 内置浅比较,性能更优 |
| 场景 | 普通业务逻辑 | 性能敏感、大型表格/表单组件 |
面试回答思路:
- Hooks 现代、灵活,适合大多数新项目
- Connect 性能稳定、自动优化,适合大型复杂组件
六、React 18 注意事项
-
React 18 默认 自动批处理(自动合并同一事件中的多次状态更新),如果您使用的是 React 18,则无需使用
batchAPI。React 18 会自动批量处理所有状态更新,无论它们在队列中的哪个位置。 -
useSelector在极端情况下可能产生“僵尸子组件”和“过时 props”,需注意:- 避免 selector 依赖可变 props
- 在 selector 中做防御性判断
Connect 内部通过嵌套 Subscription 处理这些问题,Hooks 则需要自己注意。
七、Redux + React Router
在 React Router v7 中,配合 Redux:
import { Provider } from 'react-redux';
import { RouterProvider } from 'react-router-dom';
import store from './app/store';
import router from './router';
createRoot(document.getElementById('root')!).render(
<Provider store={store}>
<RouterProvider router={router} />
</Provider>
);
八、TypeScript 最佳实践
- 每个 slice 定义状态类型和 action payload 类型
- 自定义 Hooks
useAppDispatch和useAppSelector - 使用
ConnectedProps推断 connect 组件 Props - Selector 函数返回具体类型,避免
any
九、总结
- Redux Toolkit 简化了 store、slice、reducer 的创建
- Hooks 是现代 React Redux 的推荐方式,简洁灵活
- Connect 适合性能敏感组件或老项目 Class 组件
- TypeScript 类型化 Hooks 和 ConnectedProps 可以极大提高开发效率和类型安全
- React 18 自动批处理状态更新,需要注意 selector 中依赖 props 的问题
延伸阅读
- Redux Toolkit 官方文档:redux-toolkit.js.org/
- React Redux hooks 指南:react-redux.js.org/api/hooks
- 个人demo地址:gitee.com/sue-boy/rea…