Skip to content

在react中使用redux

首先,咱们得明确一个问题:为什么在 React 中需要 Redux?

React 组件本身有自己的状态(useState),父子组件之间也能通过 props 传递数据。但当你的应用越来越大,组件层级越来越深时,你可能会遇到这些问题:

  1. Prop Drilling(逐级透传): 状态需要从顶层组件一层一层地通过 props 传递给深层子组件,代码变得非常冗长和难以维护。
  2. 兄弟组件通信困难: 两个没有直接父子关系的组件需要共享状态或互相通信时,变得非常复杂。
  3. 状态逻辑分散: 应用中的状态散落在各个组件内部,难以统一管理和调试。

Redux 就是为了解决这些问题而生的状态管理库。它提供了一个可预测的状态容器,让你的应用状态集中存放、统一管理,并以可预测的方式进行修改。

1. Redux 的核心三要素(工作流)

理解 Redux 的核心,就是理解它的单向数据流

  • Store(数据仓库):

    整个应用的

    唯一数据源

    。它是一个 JavaScript 对象,包含了应用的所有状态。

    • 就好比你家的“总账本”,所有资产负债都在这里记录。
  • Action(动作):

    一个普通的 JavaScript 对象,用于

    描述发生了什么

    。它至少包含一个

    type

    字段,表示动作的类型,可以有其他字段携带数据。

    • 好比你写的“购物清单”或“银行转账单”,只描述你“想做什么”或“发生了什么”。
    • 例子:{ type: 'ADD_TODO', text: '学习 Redux' }
  • Reducer(处理器):

    一个

    纯函数

    。它接收当前的

    state

    和一个

    action

    作为参数,然后根据

    action

    的类型,

    返回一个新的 state

    • 它绝对不能直接修改原始 state 对象!

    • 好比银行的“会计师”,接到你的转账单(Action),根据当前的账本(State),计算出新的余额,然后写在新的账本上(返回新 State)。他不会撕掉旧账本的某一页去修改。

    • 例子:

      JavaScript

      function counterReducer(state = { value: 0 }, action) {
        switch (action.type) {
          case 'INCREMENT':
            return { value: state.value + 1 };
          case 'DECREMENT':
            return { value: state.value - 1 };
          default:
            return state; // 默认情况下返回当前 state
        }
      }
  • Dispatch(派发):

    唯一触发状态更新的方式。通过调用

    store.dispatch(action)

    action

    发送给 Store。Store 会将

    action

    传递给 Reducer,由 Reducer 计算出新的状态。

    • 好比你把“购物清单”交给店家,店家开始处理。

数据流图示(口述):

你的 React 组件 (UI)
      |
      | (用户交互,例如点击按钮)
      V
Action (描述发生了什么)
      |
      | dispatch(action)
      V
Store (接收到 Action)
      |
      | 将当前 State 和 Action 传给 Reducer
      V
Reducer (纯函数,根据 Action 类型计算新 State)
      |
      | 返回新的 State
      V
Store (用新 State 更新自己)
      |
      | (Store 发生变化,通知订阅者)
      V
你的 React 组件 (UI 重新渲染,显示新 State)

2. 在 React 中集成 Redux (react-redux)

为了让 React 组件能够“感知”Redux Store 的存在并与之交互,我们需要使用官方提供的集成库 react-redux。它提供了 Hook API,让函数组件使用 Redux 变得非常简洁。

核心 Hook:

  • Provider 组件:

    这是

    react-redux

    提供的最外层组件。你需要用它包裹你的整个 React 应用(通常在

    src/index.js

    src/App.js

    ),并把你的 Redux Store 作为

    prop

    传递给它。

    • 作用: 它让所有被包裹的 React 组件都能通过 Context 访问到 Redux Store。

    JavaScript

    // src/index.js 或 src/App.js
    import React from 'react';
    import ReactDOM from 'react-dom/client'; // 或 'react-dom'
    import { Provider } from 'react-redux';
    import store from './app/store'; // 你的 Redux Store
    import App from './App';
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <Provider store={store}>
        <App />
      </Provider>
    );
  • useSelector Hook:

    在函数组件中,用于从 Redux Store 的状态中

    读取数据

    • 它接收一个“选择器”函数作为参数,这个函数会接收整个 Redux State 作为输入,并返回你需要的特定数据。
    • 当选择器返回的值发生变化时,组件会自动重新渲染。

    JavaScript

    import React from 'react';
    import { useSelector } from 'react-redux';
    
    function CounterDisplay() {
      // 从 Redux state 中选择 count.value
      const count = useSelector(state => state.counter.value); // 假设你的 reducer 名字是 'counter'
    
      return <div>当前计数: {count}</div>;
    }
  • useDispatch Hook:

    在函数组件中,用于获取

    dispatch

    函数,从而

    派发 Action

    来更新 Redux Store 的状态。

    JavaScript

    import React from 'react';
    import { useDispatch } from 'react-redux';
    
    function CounterControls() {
      const dispatch = useDispatch();
    
      return (
        <div>
          <button onClick={() => dispatch({ type: 'INCREMENT' })}>增加</button>
          <button onClick={() => dispatch({ type: 'DECREMENT' })}>减少</button>
        </div>
      );
    }

3. 现代 Redux 最佳实践:Redux Toolkit (RTK)

传统的 Redux 配置和编写方式会产生大量的“样板代码”(boilerplate),比如手动定义 Action Types、Action Creators、编写 Switch Case 臃肿的 Reducer。这让很多初学者望而却步,也让开发效率降低。

Redux Toolkit (RTK) 是 Redux 官方推荐的工具集,它旨在简化 Redux 的开发流程,减少样板代码,并提供内置的最佳实践。现在学习 Redux,强烈推荐直接从 Redux Toolkit 开始!

RTK 的核心优势:

  • 减少样板代码: 自动化创建 Action Types、Action Creators 和 Reducers。
  • 内置 Immer: 让你可以在 Reducer 中“看似”直接修改 State,而实际上 Immer 会在底层帮你生成不可变的 State。
  • 更好的开发体验: 统一的配置和模块化的设计。

RTK 核心 API:

  • configureStore 替代传统的 createStore,提供更好的默认配置(如 Redux DevTools 集成、redux-thunk 中间件等)。
  • createSlice 这是 RTK 的“明星”API。它允许你定义一个“切片”(slice),一个切片就包含了 nameinitialStatereducers(处理同步逻辑)和 extraReducers(处理异步逻辑)等。它会自动生成 Action Creators 和 Reducer。
  • RTK Query: (简要提及) RTK Query 是 RTK 的一部分,专门用于简化数据获取和缓存,几乎可以替代很多场景下的 redux-thunkredux-saga

使用 createSlice 的例子 (Counter 应用):

  1. 定义 Slice:

    JavaScript

    // src/features/counter/counterSlice.js
    import { createSlice } from '@reduxjs/toolkit';
    
    export const counterSlice = createSlice({
      name: 'counter', // slice 的名称,也会用于生成 action type 前缀
      initialState: {
        value: 0,
      },
      reducers: {
        // 在这里定义同步的 action 和对应的 reducer 逻辑
        increment: (state) => {
          // Immer 使得你可以“直接”修改 state,底层会自动生成新状态
          state.value += 1;
        },
        decrement: (state) => {
          state.value -= 1;
        },
        incrementByAmount: (state, action) => {
          state.value += action.payload; // action.payload 是传递过来的数据
        },
      },
    });
    
    // 导出 action creators
    export const { increment, decrement, incrementByAmount } = counterSlice.actions;
    
    // 导出 reducer
    export default counterSlice.reducer;
  2. 配置 Store:

​ // src/app/store.js

import { configureStore } from '@reduxjs/toolkit';

import counterReducer from '../features/counter/count1erSlice'; // 导入上面定义的 reducer

export default configureStore({
  reducer: {
    counter: counterReducer, // 将 reducer 挂载到 store 的 counter 属性上
    // 可以有多个 reducer,每个对应一个应用的状态切片
  },
});
```
  1. 在 React 组件中使用:

    JavaScript

    // src/features/counter/Counter.js
    import React from 'react';
    import { useSelector, useDispatch } from 'react-redux';
    import { increment, decrement, incrementByAmount } from './counterSlice'; // 导入 action creators
    
    export function Counter() {
      const count = useSelector((state) => state.counter.value); // 从 store 中获取 counter.value
      const dispatch = useDispatch();
    
      return (
        <div>
          <p>当前计数: {count}</p>
          <div>
            <button onClick={() => dispatch(increment())}>增加</button> {/* 直接调用 action creator */}
            <button onClick={() => dispatch(decrement())}>减少</button>
            <button onClick={() => dispatch(incrementByAmount(5))}>增加 5</button> {/* 传递 payload */}
          </div>
        </div>
      );
    }

4. 何时使用 Redux?

虽然 Redux 功能强大,但并不是所有 React 应用都需要它。

推荐使用 Redux 的场景:

  • 应用状态复杂且庞大: 多个组件需要共享大量状态,或者状态更新逻辑非常复杂。
  • 状态变化频繁且难以追踪: 需要明确地知道状态是如何以及何时改变的,方便调试。
  • 需要时间旅行调试、状态快照等高级调试功能。
  • 应用规模较大,需要统一的状态管理规范。
  • 需要处理大量异步数据流(配合 Redux Thunk 或 Redux Saga / RTK Query)。

不推荐使用 Redux 的场景:

  • 小型应用或简单组件: 仅仅是父子组件之间的状态传递,或者组件内部的简单状态,useStateuseContext 就足够了,没必要引入 Redux 增加复杂度。
  • 只关心 UI 状态(如弹窗是否打开): 这些状态通常可以放在组件内部管理。

总结与鼓励

Redux 及其现代化的 Redux Toolkit,为 React 应用提供了强大且可预测的状态管理解决方案。理解它的核心概念、单向数据流,并熟练运用 react-redux 的 Hook API,特别是拥抱 Redux Toolkit,将大大提升你开发大型复杂 React 应用的能力。

别被一开始的 Redux 概念图吓到,只要跟着 RTK 的最佳实践走,你会发现 Redux 变得非常直观和高效。多写代码,多思考状态的流转,你很快就能驾驭 Redux,让你的 React 应用状态管理得井井有条,丝滑流畅!

One Day We Will All Be Famous!