react-redux,you are under arrest.

66 阅读3分钟

Nick Wilde, you're under arrest for‌ felony tax evasion.
捕个der,还好狐狸和兔子有生殖隔离,不然我要被气鼠。
本文系统逮捕 react-redux 的使用方法,包括最新的 Redux Toolkit、TypeScript 类型化、connect 高阶组件和 hooks 用法,以及 React 18 特性相关注意事项。


u=171264730,3618199696&fm=253&app=138&f=JPEG.jpg

一、安装与项目初始化

首先,创建一个 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 中间件。

image.png

2. 在 React 中提供 Store

在入口文件 src/main.tsxsrc/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,并进行浅比较优化,适合性能敏感的大型组件。

image.png

image.png


五、Hooks 与 Connect 区别

特性Hooks (useSelector/useDispatch)Connect 高阶组件
写法简单、现代冗长,但清晰
TS 类型需自定义 HooksConnectedProps 自动推断
性能可能无意中多次渲染,需要 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 最佳实践

  1. 每个 slice 定义状态类型和 action payload 类型
  2. 自定义 Hooks useAppDispatchuseAppSelector
  3. 使用 ConnectedProps 推断 connect 组件 Props
  4. Selector 函数返回具体类型,避免 any

九、总结

  • Redux Toolkit 简化了 store、slice、reducer 的创建
  • Hooks 是现代 React Redux 的推荐方式,简洁灵活
  • Connect 适合性能敏感组件或老项目 Class 组件
  • TypeScript 类型化 Hooks 和 ConnectedProps 可以极大提高开发效率和类型安全
  • React 18 自动批处理状态更新,需要注意 selector 中依赖 props 的问题

延伸阅读