Redux 与 React-Redux:从“全局变量”到“响应式状态”的优雅之道

大家好,我是小杨。还记得刚开始学 React 时,遇到复杂的组件状态共享问题,prop drilling(属性钻取)传得我头晕眼花。直到我遇到了 Redux,它就像一个"全局状态保险箱",让数据管理变得清晰可控。

但光有 Redux 还不够,如何让 React 组件和这个"保险箱"优雅地对话呢?这就是 React-Redux 的功劳了。今天,我就用一个简单的例子,帮你彻底理清这两者的关系和用法。

打个比方:Redux 是仓库,React-Redux 是送货员

想象一下我们的应用是一个大公司:

  • Redux : 公司的中央仓库(Store) 。它有着非常严格的规矩:所有存取货物的操作都必须通过提交一张标准的申请单(Action)管理员(Reducer) ,管理员根据申请单的类型(Type)来更新仓库里的货物(State)。你不能直接去仓库里乱翻。
  • React-Redux : 公司的送货员(Provider & useSelector/useDispatch) 。它的工作是把仓库里的货物(State)高效地送达(订阅并更新) 到需要的各个部门(组件),并且把各部门的存取需求(派发 Action)准确地传达给仓库。

没有送货员,各部门就得自己跑腿去仓库取货,效率低下且容易出错。有了送货员,各部门只需说明需要什么,剩下的交给它就行。

核心概念快速回顾

在写代码前,我们快速过一下三个核心概念,这也是 Redux 的"三大纪律":

  1. Store: 状态容器,整个应用只有一个,存储着所有共享状态。
  2. Action: 一个普通的 JavaScript 对象,用来描述"发生了什么"。它是改变 State 的唯一途径。
  3. Reducer: 一个纯函数,它接收当前的 State 和一个 Action,根据 Action 的类型来决定如何更新 State,并返回全新的 State。

实战:构建一个简单的计数器

我们来亲手搭建一个使用 Redux 和 React-Redux 的计数器应用。

第一步:安装它们

bash 复制代码
npm install @reduxjs/toolkit react-redux

(我们使用现代化的 Redux Toolkit 来简化开发)

第二步:创建"仓库"和"管理员"(Slice)

首先,我们定义仓库的规则。创建一个文件 store/slices/counterSlice.js

jsx 复制代码
import { createSlice } from '@reduxjs/toolkit';

// 使用 createSlice 来自动生成 action creators 和 reducer
const counterSlice = createSlice({
  name: 'counter', // slice 的名称
  initialState: {
    value: 0, // 初始状态
  },
  reducers: {
    // 这里定义的都是" reducer 函数"
    incremented: (state) => {
      // Redux Toolkit 允许我们直接在原 state 上"修改",
      // 因为它内部使用了 Immer,实际上会生成新的不可变状态。
      state.value += 1;
    },
    decremented: (state) => {
      state.value -= 1;
    },
    // 一个带参数的 action
    incrementByAmount: (state, action) => {
      state.value += action.payload;
    },
  },
});

// 自动生成的 action creators
// 例如:counterSlice.actions.incremented() 返回 { type: 'counter/incremented' }
export const { incremented, decremented, incrementByAmount } = counterSlice.actions;

// 导出 reducer 函数,用于配置 store
export default counterSlice.reducer;

第三步:创建仓库(Store)

创建 store/index.js 文件,将所有的 reducer 组合起来。

jsx 复制代码
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './slices/counterSlice';

// 创建 Redux Store,并传入我们的 reducer
export const store = configureStore({
  reducer: {
    counter: counterReducer, // 这里的 `counter` 是状态树的一个分支
    // 未来可以在这里添加其他 slice 的 reducer
  },
});

// 导出 RootState 和 AppDispatch 类型,方便在 TypeScript 项目中使用
// export type RootState = ReturnType<typeof store.getState>;
// export type AppDispatch = typeof store.dispatch;

第四步:派遣"送货员"到公司(Provider)

这是 React-Redux 出场的第一步。我们需要在应用的顶层用 <Provider> 组件包裹,并把创建好的 store 传给它。这样,所有子组件才能访问到 store。

jsx 复制代码
// index.js 或 App.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux'; // 引入送货员总部
import { store } from './store'; // 引入我们创建的中心仓库
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    {/* 用 Provider 包裹整个应用,并传入 store */}
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

第五步:在组件里"收发货物"(useSelector & useDispatch)

现在,任何组件都可以和 Redux Store 交互了。我们使用 React-Redux 提供的两个 Hook:

  • useSelector: 从仓库里"取"状态。它订阅了状态的变化,状态一变,组件会自动重新渲染。
  • useDispatch: 获取 dispatch 函数,用于"提交申请单"(派发 Action)。
jsx 复制代码
// components/Counter.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { incremented, decremented, incrementByAmount } from '../store/slices/counterSlice';

export function Counter() {
  // 1. 使用 useSelector "取货"
  // 参数是一个 selector 函数,从整个 state 中选取需要的部分
  const count = useSelector((state) => state.counter.value);

  // 2. 使用 useDispatch 获取 dispatch 函数,用于"提交申请"
  const dispatch = useDispatch();

  return (
    <div>
      <div>
        <span>当前计数: {count}</span>
        <br />
        {/* 3. 点击按钮,派发 Action */}
        <button onClick={() => dispatch(incremented())}>+1</button>
        <button onClick={() => dispatch(decremented())}>-1</button>
        <button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
      </div>
    </div>
  );
}

总结:它们是如何协作的?

  1. Redux (@reduxjs/toolkit) : 负责状态管理本身。它定义了状态的结构、如何通过 reducer 更新状态、以及生成 action。
  2. React-Redux : 负责 React 和 Redux 之间的连接<Provider> 使 store 对组件树可用,useSelectoruseDispatch 则让组件能够读取状态和触发更新。

简单来说,Redux 是管理状态的大脑,而 React-Redux 是连接大脑和四肢(组件)的神经网络。两者缺一不可,共同构成了一个高效、可预测的状态管理体系。

虽然现在有 Context API、Zustand 等更多选择,但理解 Redux 的设计思想依然非常有价值。希望这篇文章能帮你解开对它们的困惑!

⭐ 写在最后

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅ 认为我部分代码过于老旧,可以提供新的API或最新语法

✅ 对于文章中部分内容不理解

✅ 解答我文章中一些疑问

✅ 认为某些交互,功能需要优化,发现BUG

✅ 想要添加新功能,对于整体的设计,外观有更好的建议

✅ 一起探讨技术加qq交流群:906392632

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!

相关推荐
wycode5 分钟前
# 面试复盘(2)--某硬件大厂前端
前端·面试
怪可爱的地球人7 分钟前
ts枚举(enum)
前端
做你的猫9 分钟前
深入剖析:基于Vue 3与Three.js的3D知识图谱实现与优化
前端·javascript·vue.js
渊不语13 分钟前
富文本编辑器自定义图片等工具栏-完整开发文档
前端
用户239712822487014 分钟前
taro+vue3+vite项目 tailwind 踩坑记,附修复后的模板源码地址
前端
做你的猫18 分钟前
深入剖析:基于Vue 3的高性能AI聊天组件设计与实现
前端·javascript·vue.js
G佳伟20 分钟前
vue拖动排序,vue使用 HTML5 的draggable拖放 API实现内容拖并排序,并更新数组数据
前端·vue.js·html5
Bling_Bling_125 分钟前
ES6新语法特性(第二篇)
开发语言·前端·es6
石小石Orz37 分钟前
妙啊!Js的对象属性居然还能用这么写
前端
成熟的API调用专家43 分钟前
cesium 获取鼠标点击位置的经度纬度海拔高度
前端