理解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 决策的关键依据。
    • 通过区分不同的 typereducer 可以执行相应的逻辑来更新状态。
  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')
);
相关推荐
小张快跑。1 分钟前
【Vue3】(三)vue3中的pinia状态管理、组件通信
前端·javascript·vue.js
我想说一句1 分钟前
当 map 遇上 parseInt:JS 中一场参数引发的“血案”
前端·javascript·面试
陈随易2 分钟前
2025年100个产品计划之第12个(杰森排序) - 对 JSON 属性进行排序
前端·后端·程序员
LeeAt2 分钟前
《谁杀死了比尔?》:使用Trae完成的一个推理游戏项目!!
前端·游戏开发·trae
Hockor6 分钟前
写给前端的 Python 教程四(列表/元组)
前端·后端·python
GetcharZp7 分钟前
「DPlayer」超强弹幕视频播放器来了!支持m3u8直播,5分钟搞定集成!
前端
天天码行空10 分钟前
Bootstrap Table企业级web数据表格集成框架
前端·javascript·开源
import_random14 分钟前
[关联规则]apriori算法和fp-growth算法(区别)
前端
lyc23333318 分钟前
鸿蒙IME Kit高级开发:共享沙箱与跨进程数据传输🚀
前端
lyc23333318 分钟前
鸿蒙UTD详解:标准化数据类型的跨端协作密钥🔑
前端