大家好,我是小杨。还记得刚开始学 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 的"三大纪律":
- Store: 状态容器,整个应用只有一个,存储着所有共享状态。
- Action: 一个普通的 JavaScript 对象,用来描述"发生了什么"。它是改变 State 的唯一途径。
- 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>
);
}
总结:它们是如何协作的?
- Redux (@reduxjs/toolkit) : 负责状态管理本身。它定义了状态的结构、如何通过 reducer 更新状态、以及生成 action。
- React-Redux : 负责 React 和 Redux 之间的连接 。
<Provider>
使 store 对组件树可用,useSelector
和useDispatch
则让组件能够读取状态和触发更新。
简单来说,Redux 是管理状态的大脑,而 React-Redux 是连接大脑和四肢(组件)的神经网络。两者缺一不可,共同构成了一个高效、可预测的状态管理体系。
虽然现在有 Context API、Zustand 等更多选择,但理解 Redux 的设计思想依然非常有价值。希望这篇文章能帮你解开对它们的困惑!
⭐ 写在最后
请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.
✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式
✅ 认为我部分代码过于老旧,可以提供新的API或最新语法
✅ 对于文章中部分内容不理解
✅ 解答我文章中一些疑问
✅ 认为某些交互,功能需要优化,发现BUG
✅ 想要添加新功能,对于整体的设计,外观有更好的建议
✅ 一起探讨技术加qq交流群:906392632
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!