理解Redux

自从开始学习redux的用法后,感觉就是硬往脑子里塞语法,也不知道为什么要这样做,今天想用一个贴近生活的例子来帮助理解redux的设计和用法。

我们先来介绍几个概念:

Store :仓库,储存数据的地方;
Action :动作/事件;
Reducer :应用程序状态的变化方式,它接受旧状态和动作,返回一个新状态;
Dispatch :分发器,将action发送至reducer;
Subscribe:订阅器,当store发生改变时,订阅者会收到通知并执行更新操作;

我们把redux store比作一个图书管理系统,store就是这个图书馆的数据库,存储着所有的图书信息和借阅记录,每次我们需要查询数据状态或者借还书的时候,都需要和这个数据库做交互,比如说我想要借书,这一个事件可能会包含以下信息:

  • Action Type:Borrow_Book
  • Payload:{ bookId: 'B001', borrowId: 'A001'}

这些事件信息会由图书管理员(dispatch)整理给数据更新员(reducer),再提交给图书数据库;这时图书管理员就会承担起数据库与我们之间沟通的桥梁,所以我们的事件实际上都是由这个数据更新员来实现的,而这个管理员在redux里就是reducer的存在,他们会根据申请单的类型和内容,决定如何更新图书馆数据库的书籍状态和借阅记录。

而reducer有以下几个特点:

  • 纯函数:对于相同的输入(状态和动作),总是返回相同的结果;
  • 不可变性:每次更新状态时,都不是在直接对数据库进行修改,而是创建一个全新的状态对象,再进行修改,修改成功后再替换旧状态;(有没有感觉和react的组件不可变性有共通之处)

当图书管理员接收到一个借书事件时,会检查数据库中该书籍的可借状态和剩余数量,然后在新生成的数据库状态中记录这次借阅,更新书籍的可借状态和剩余数量,最后将更新后的数据库状态返回给图书数据库。

整体流程

  1. 读者提交申请:调用dispatch,提交action;
  2. 管理员处理:reducer处理action,更新state;
  3. 更新数据库;
  4. 通知查询系统;

补充

Q:为什么要通过dispatch将action传递给reducer?

A:在redux中,dispatch至关重要,它提供了一个公共的入口供组件和外部文件触发状态更新,避免直接访问或操作reducer;无论何时何地需要更新状态,只需调用dispatch函数并传入相应的action。另外,dispatch也可以更好的插入和使用中间件;最后,dispatch会在函数内部实现订阅者通知(这个后面会提到)

Q:action type 和 payload 是如何帮助 reducer 精确地知道需要做什么的?

A:

  1. Action Type:

    • 定义了发生什么类型的事件,是 reducer 决策的关键依据。
    • 通过区分不同的 type,reducer 可以执行相应的逻辑来更新状态。
  2. Action Payload:

    • 提供了执行操作所需的具体数据。
    • 在 reducer 中,payload 用来获取必要的信息,以便精确地更新状态。

Q:发布-订阅者模式是什么?redux里又是怎么实现的?

A:定义了一个一对多的依赖关系,即发布者->订阅者,每当发布者状态发生改变时,所有订阅者的都会得到通知,并且可以执行相应的更新逻辑。

在redux中,

  1. store相当于发布者,它维护着应用的全局状态,并提供subscribe方法供其他对象使用来订阅状态的变化。
  2. 组件作为订阅者,通过useSelector订阅store的状态,当store的状态发生改变时,dispatch函数的执行流程会继续执行订阅者通知的步骤,进而触发组件的重新渲染。

只讲概念太抽象了,结合代码看一下吧,举个最简单的计数器例子:

javascript 复制代码
//store.js  
import { configureStore } from '@reduxjs/toolkit';
import conterSlice from './counterSlice'
export default store = configStore({
    reducer: {
        counter: counterSlice
    }
})
javascript 复制代码
// counterSlice.js   
import { createSlice } from '@reduxjs/toolkit';  

export const counterSlice = createSlice({
    name: 'count'  
    initalState: {
        counter: 0;
    }
    reducers: {
        increment: state => { state.value += 1; }, 
        decrement: state => { state.value -= 1; }, 
        incrementByAmount: (state, action) => { state.value += action.payload; }
    }
})
  
export default counterSlice.reducer  
// 导出每个 reducer 函数作为 action  
export const { increment, decrement, incrementByAmount } from counterSlice.actions
javascript 复制代码
// App.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, incrementByAmount } from './counterSlice';

function App() {
 const count = useSelector(state => state.counter.value);
 const dispatch = useDispatch();

 return (
   <div>
     <div>Count: {count}</div>
     <button onClick={() => dispatch(increment())}>Increment</button>
     <button onClick={() => dispatch(decrement())}>Decrement</button>
     <button onClick={() => dispatch(incrementByAmount(5))}>Increment by 5</button>
   </div>
 );
}

export default App;
javascript 复制代码
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import App from './App';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
相关推荐
deli0075 分钟前
32K 上下文塞满后,AI 最先忘掉什么?120 轮对话实测
前端
deli0071 小时前
手机拍灯为什么有一条条黑纹?PWM 240Hz 实测
前端
wangruofeng6 小时前
AI Models 1.1.0 发布,查大模型价格、对比模型和找 API 地址
前端·aigc·ai编程
wangruofeng6 小时前
给前端开发者的 100 个设计参考网站
前端·设计
星栈9 小时前
视频即代码,Remotion 能做什么、不能做什么
前端·音视频开发
广州华水科技9 小时前
单北斗GNSS位移监测系统的应用与技术解析
前端
用户47949283569159 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试
风骏时光牛马12 小时前
生成一个AI多模态标题,仅输出一条标题,无额外文字、问句、备选、配图、文案等任何附加内容,不需要其他的任务服务 只要标题。
前端
wangruofeng12 小时前
从 GIF 到 AVIF:网页图片格式演进与 2026 年选型指南
前端·架构
清晨Coding13 小时前
为什么用 React Developer Tools 查看组件树这么乱?
前端·react.js