【React】状态管理之Redux

news2024/11/14 14:49:37

鑫宝Code

🌈个人主页: 鑫宝Code
🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础
💫个人格言: "如无必要,勿增实体"


文章目录

  • 状态管理之Redux
    • 引言
    • 1. Redux 的核心概念
      • 1.1 单一数据源(Single Source of Truth)
      • 1.2 State 是只读的
      • 1.3 使用纯函数执行修改
    • 2. Redux 工作流程
      • 2.1 数据流向
      • 2.2 中间件机制
    • 3. Redux 内部实现原理
      • 3.1 createStore 的实现
      • 3.2 combineReducers 的实现
    • 4. Redux 性能优化
      • 4.1 reselect 的使用
      • 4.2 不可变性的保持
    • 5. 实际应用中的最佳实践
      • 5.1 Action 创建函数
      • 5.2 异步 Action 处理
    • 总结

状态管理之Redux

在这里插入图片描述

引言

Redux 作为一个优秀的状态管理工具,在 React 生态系统中占据着重要地位。本文将深入探讨 Redux 的核心工作原理,帮助开发者更好地理解和使用这个工具。

1. Redux 的核心概念

1.1 单一数据源(Single Source of Truth)

Redux 使用单一的 store 来存储应用的所有状态。这意味着:

  • 整个应用的状态被存储在一个对象树中
  • 这个对象树只存在于唯一的 store 中
  • 状态是只读的,唯一改变状态的方式是触发 action
const store = {
  todos: [],
  visibilityFilter: 'SHOW_ALL',
  user: {
    id: null,
    name: null
  }
}

1.2 State 是只读的

在 Redux 中,改变状态的唯一方式是触发(dispatch)一个 action。这确保了:

  • 视图和网络请求都不能直接修改状态
  • 所有的修改都被集中化处理
  • 修改都是按顺序一个接一个地执行
// Action 的结构
const action = {
  type: 'ADD_TODO',
  payload: {
    text: '学习 Redux',
    completed: false
  }
}

1.3 使用纯函数执行修改

Reducer 是一个纯函数,它接收先前的状态和一个 action,返回新的状态:

const todoReducer = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [...state, action.payload]
    case 'TOGGLE_TODO':
      return state.map(todo =>
        todo.id === action.payload.id
          ? { ...todo, completed: !todo.completed }
          : todo
      )
    default:
      return state
  }
}

2. Redux 工作流程

在这里插入图片描述

2.1 数据流向

Redux 采用严格的单向数据流,主要包含以下步骤:

  1. 用户在界面触发事件
  2. 调用 dispatch(action)
  3. Redux store 调用 reducer 函数
  4. Root reducer 把多个子 reducer 输出合并成一个单一的状态树
  5. Redux store 保存了 reducer 返回的完整状态树

2.2 中间件机制

中间件提供了一个分类处理 action 的机制:

// 中间件示例
const logger = store => next => action => {
  console.log('dispatching', action)
  let result = next(action)
  console.log('next state', store.getState())
  return result
}

3. Redux 内部实现原理

3.1 createStore 的实现

createStore 是 Redux 最核心的 API:

function createStore(reducer, preloadedState, enhancer) {
  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  
  function getState() {
    return currentState
  }
  
  function subscribe(listener) {
    currentListeners.push(listener)
    return function unsubscribe() {
      const index = currentListeners.indexOf(listener)
      currentListeners.splice(index, 1)
    }
  }
  
  function dispatch(action) {
    currentState = currentReducer(currentState, action)
    currentListeners.forEach(listener => listener())
    return action
  }
  
  return {
    getState,
    subscribe,
    dispatch
  }
}

3.2 combineReducers 的实现

combineReducers 用于合并多个 reducer:

function combineReducers(reducers) {
  return function combination(state = {}, action) {
    const nextState = {}
    let hasChanged = false
    
    for (let key in reducers) {
      const reducer = reducers[key]
      const previousStateForKey = state[key]
      const nextStateForKey = reducer(previousStateForKey, action)
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    
    return hasChanged ? nextState : state
  }
}

4. Redux 性能优化

在这里插入图片描述

4.1 reselect 的使用

使用 reselect 可以避免不必要的重复计算:

import { createSelector } from 'reselect'

const getTodos = state => state.todos
const getVisibilityFilter = state => state.visibilityFilter

const getVisibleTodos = createSelector(
  [getTodos, getVisibilityFilter],
  (todos, filter) => {
    switch (filter) {
      case 'SHOW_ALL':
        return todos
      case 'SHOW_COMPLETED':
        return todos.filter(t => t.completed)
      case 'SHOW_ACTIVE':
        return todos.filter(t => !t.completed)
    }
  }
)

4.2 不可变性的保持

确保状态更新的不可变性是 Redux 性能优化的关键:

// 不推荐
state.todos[0].completed = true

// 推荐
return {
  ...state,
  todos: state.todos.map((todo, index) =>
    index === 0 ? { ...todo, completed: true } : todo
  )
}

5. 实际应用中的最佳实践

5.1 Action 创建函数

使用 action 创建函数来生成 action:

const addTodo = text => ({
  type: 'ADD_TODO',
  payload: {
    id: nextTodoId++,
    text,
    completed: false
  }
})

5.2 异步 Action 处理

使用 redux-thunk 处理异步操作:

const fetchTodos = () => {
  return async dispatch => {
    dispatch({ type: 'FETCH_TODOS_REQUEST' })
    try {
      const response = await api.fetchTodos()
      dispatch({
        type: 'FETCH_TODOS_SUCCESS',
        payload: response.data
      })
    } catch (error) {
      dispatch({
        type: 'FETCH_TODOS_FAILURE',
        error: error.message
      })
    }
  }
}

总结

Redux 通过其简单而强大的设计原则,为 React 应用提供了可预测的状态管理能力。理解其工作原理对于构建大型应用至关重要。核心要点包括:

  • 单一数据源
  • 状态只读
  • 使用纯函数进行修改
  • 单向数据流
  • 中间件机制

通过合理运用这些原则,我们可以构建出更加可维护和可扩展的应用。同时,通过使用 reselect、保持不可变性等优化手段,还能确保应用具有良好的性能表现。

End

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2240239.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数字IC后端实现之Innovus specifyCellEdgeSpacing和ICC2 set_placement_spacing_rule的应用

昨天帮助社区IC训练营学员远程协助解决一个Calibre DRC案例。通过这个DRC Violation向大家分享下Innovus和ICC2中如何批量约束cell的spacing rule。 数字IC后端手把手实战教程 | Innovus verify_drc VIA1 DRC Violation解析及脚本自动化修复方案 下图所示为T12nm A55项目的Ca…

LLM - 计算 多模态大语言模型 的参数量(Qwen2-VL、Llama-3.1) 教程

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/143749468 免责声明:本文来源于个人知识与公开资料,仅用于学术交流,欢迎讨论,不支持转载。 影响 (…

一图胜千言,一张图深入读懂大模型应用是如何工作的

在科技飞速发展的今天,人工智能(AI)早已不再是遥不可及的科幻概念,而是融入了我们生活的方方面面。其中,大模型作为AI领域的重要分支,以其卓越的表现力和广泛的应用前景,吸引了无数人的目光。但…

Spring AI Alibaba - 快速开发生成式Java Al应用

大家好,我是袁庭新。 今天我们不谈ServerlessAI、AI可观测性、云消息队列演进与AI赋能以及AI原生应用架构等,这些都是近年最火热的技术方向。但是如果你想在未来成为一名合格且具备前瞻视野的软件开发工程师,这些新兴且热门的技术领域都是需…

简易入手《SOM神经网络》的本质与原理

原创文章,转载请说明来自《老饼讲解神经网络》:www.bbbdata.com 关于《老饼讲解神经网络》: 本网结构化讲解神经网络的知识,原理和代码。 重现matlab神经网络工具箱的算法,是学习神经网络的好助手。 目录 一、入门原理解说 01.…

大模型经典著作《大语言模型基础与前沿》

介绍 **《大语言模型基础与前沿》是由美国明尼苏达大学双城分校电子与计算机工程博士熊涛所著。**熊博士曾在多家中美知名高科技公司担任高级管理职位和首席科学家,在人工智能的多个领域,包括大语言模型、图神经网络等从事研发和管理工作多年。 本书深…

DBeaver 连接 OceanBase Oracle 租户

DBeaver 是一款通用的数据库工具软件,支持任何具有JDBC驱动程序的数据库。DBeaver 需要 Java 运行环境的支持。截稿时 DBeaver 24.0.0 版本默认提供的 OceanBase 驱动是连接 MySQL 的,想连接 Oracle 租户需要新建一个驱动器使用。 下载数据库驱动包 1、…

定时任务进行简单监控、爬虫的自动化之旅

原文链接:「定时任务」进阶指南:监控、爬虫的自动化之旅

spring gateway 动态路由

##yml配置 spring:application:name: public-gateway # cloud: # gateway: # routes: # - id: mybatis-plus-test # 路由的唯一标识 # uri: http://192.168.3.188:9898 # 目标服务的地址 # predicates: # - Path/test/** # 匹配…

论文1—《基于卷积神经网络的手术机器人控制系统设计》文献阅读分析报告

论文报告:基于卷积神经网络的手术机器人控制系统设计 摘要 本研究针对传统手术机器人控制系统精准度不足的问题,提出了一种基于卷积神经网络的手术机器人控制系统设计。研究设计了控制系统的总体结构,并选用PCI插槽上直接内插CAN适配卡作为上…

OpenHarmony-1.启动流程

OpenHarmony启动流程 1.kernel的启动 流程图如下所示:   OpenHarmony(简称OH)的标准系统的底层系统是linux,所以调用如下代码: linux-5.10/init/main.c: noinline void __ref rest_init(void) {struct task_struct *tsk;int pid;rcu_sch…

Python Plotly 库使用教程

Python Plotly 库使用教程 引言 数据可视化是数据分析中至关重要的一部分,它能够帮助我们更直观地理解数据、发现潜在的模式和趋势。Python 提供了多种数据可视化库,其中 Plotly 是一个功能强大且灵活的库,支持交互式图表的创建。与静态图表…

校园交友系统的设计与实现(开源版+三端交付+搭建+售后)

系统基础架构 采用UniApp进行开发,UniApp是一个使用Vue.js开发所有前端应用的框架,它支持编译为H5、小程序、App等多个平台。 使用PHP作为后端开发语言,PHP是一种广泛使用的开源脚本语言,尤其适用于Web开发,并可高效…

SQL 外连接

1 外连接 外连接是一种用于结合两个或多个表的方式,返回至少一个表中的所有记录。 左外连接 LEFT JOIN,左表为驱动表,右表为从表。返回驱动表的所有记录以及从表中的匹配记录。如果从表没有匹配,则结果中从表的部分为NULL。 右…

死磕grass平台

Grass平台:重塑互联网价值与AI数据采集的革新之路 引言:互联网资源的新范式 在当今数字时代,大多数互联网用户面临着一个共同但鲜少被关注的现象:我们付费购买的带宽资源往往没有被充分利用。想象一下,当你订购了100 Mbps的网络服务,在浏览新闻或查看邮件时,实际可…

Spring boot + Vue2小项目基本模板

Spring boot Vue2小项目基本模板 基本介绍基本环境安装项目搭建最终效果展示 基本介绍 项目来源哔哩哔哩的青戈,跟着学习搭建自己的简单vue小项目;看别人的项目总觉得看不懂,需要慢慢打磨 这里目前只简单的搭建了菜单导航和表格页面&#x…

大数据面试题--kafka夺命连环问(后10问)

目录 16、kafka是如何做到高效读写? 17、Kafka集群中数据的存储是按照什么方式存储的? 18、kafka中是如何快速定位到一个offset的。 19、简述kafka中的数据清理策略。 20、消费者组和分区数之间的关系是怎样的? 21、kafka如何知道哪个消…

用vscode编写verilog时,如何有信号定义提示、信号定义跳转(go to definition)、模块跳转这些功能

(一)安装插件SystemVerilog - Language Support 安装一个vscode插件即可,插件叫SystemVerilog - Language Support。虽然说另一个插件“Verilog-HDL/SystemVerilog/Bluespec SystemVerilog”也有信号提示及定义跳转功能,但它只能提…

万字长文解读深度学习——Transformer

🌺历史文章列表🌺 深度学习——优化算法、激活函数、归一化、正则化深度学习——权重初始化、评估指标、梯度消失和梯度爆炸深度学习——前向传播与反向传播、神经网络(前馈神经网络与反馈神经网络)、常见算法概要汇总万字长文解读…

Leecode热题100-35.搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2示例 2: 输入:…