在react中使用redux并实现计数器案例

2023-11-13

React + Redux

在recat中不使用redux 时遇到的问题

在react中组件通信的数据是单向的,顶层组件可以通过props属性向下层组件传递数据,而下层组件不能向上层组件传递数据,要实现下层组件修改数据,需要上层组传递修改数据的方法到下层组件,当项目越来越的时候,组件之间传递数据变得越来越困难

img

在react中加入redux 的好处

使用redux管理数据,由于Store独立于组件,使得数据管理独立于组件,解决了组件之间传递数据困难的问题

img

使用redux

下载redux

npm install redux react-redux

redux 工作流程

  1. 组件通过 dispatch 触发action
  2. store 接受 action 并将 action 分发给 reducer
  3. reducer 根据 action 类型对状态进行更改并将更改后的数据返回给store
  4. 组件订阅了store中的状态,store中的状态更新会同步到组件

img

使用react+redux实现计数器

  1. 创建项目,并安装 redux
# 如果没有安装react脚手架则执行这条命令安装reate脚手架
npm install -g create-react-app
# 创建reate项目
create-react-app 项目名
# 进入项目
cd 项目名
# 安装 redux
npm install redux reate-redux
  1. 引入redux,并根据开始实现的代码在react中实现计数器
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore } from 'redux';

const initialState = {
  count: 0
}
function reducer(state = initialState, action) {
  switch (action.type) {
    case 'increment':
      return {
        count: state.count + 1
      }
    case 'decrement':
      return {
        count: state.count - 1
      }

    default:
      return state
  }
}
const store = createStore(reducer)

const increment = {
  type: 'increment'
}

const decrement = {
  type: 'decrement'
}

function Count() {
  return <div>
    <button onClick={() => store.dispatch(increment)}>+</button>
    <span>{store.getState().count}</span>
    <button onClick={() => store.dispatch(decrement)}>-</button>
  </div>
}

store.subscribe( () => {
  console.log(store.getState())
  ReactDOM.render(
    <React.StrictMode>
      <Count />
    </React.StrictMode>,
    document.getElementById('root')
  );
})

ReactDOM.render(
  <React.StrictMode>
    <Count />
  </React.StrictMode>,
  document.getElementById('root')
);

明显以上方式虽然可以实现计数器的功能,但在实际项目中肯定不能这样使用,因为组件一般都在单独的文件中的,这种方式明显在其他组件中并不能获取到Store。

计数器案例代码优化-让store全局可访问

为了解决Store获取问题需要使用react-redux来解决这个问题,react-redux给我们提供了Provider组件和connect方法

  • Provide 组件

是一个组件 可以吧创建出来的store 放在一个全局的地方,让组件可以拿到store,通过provider组件,将 store 放在了全局的组件可以够的到的地方 ,provider要求我们放在最外层组件

  • connect

connect 帮助我们订阅store中的状态,状态发生改变后帮助我们重新渲染组件

通过 connect 方法我们可以拿到 store 中的状态 把 store 中的状态映射到props中

通过 connect 方法可以拿到 dispatch 方法

connect 的参数为一个函数 这个函数可以拿到store中的状态,要求我们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性

connect 调用后返回一个函数 返回的这个函数继续调用需要传入组件告诉connect需要映射到那个组件的props

  1. 新建 Component 文件夹、创建 Count.js 文件
import React from 'react'

function Count() {
    return <div>
        <button onClick={() => store.dispatch(increment)}>+</button>
        <span>{store.getState().count}</span>
        <button onClick={() => store.dispatch(decrement)}>-</button>
    </div>
}

export default Count
  1. 引入 Provider 组件放置在最外层,并制定store
ReactDOM.render(
  // 通过provider组件 将 store 放在了全局的组件可以够的到的地方  provider要求我们放在最外层组件
  <Provider store={store}><Count /></Provider>,
  document.getElementById('root')
);
  1. 引入 connect 方法 根据 connect 的使用来包裹组件
const mapStateProps = state => ({
    count: state.count,
    a: '1'
})
// connect 的参数为一个函数 这个函数可以拿到store中的状态,要求我们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性
// connect 调用后返回一个函数 返回的这个函数继续调用需要传入组件告诉connect需要映射到那个组件的props
export default connect(mapStateProps)(Count)
  1. 改造 Count 组件把 action 复制到该文件中
const increment = {
    type: 'increment'
}

const decrement = {
    type: 'decrement'
}
function Count({count,dispatch}) {
    return <div>
        <button onClick={() => {dispatch(increment)}}>+</button>
        <span>{count}</span>
        <button onClick={() => {dispatch(decrement)}}>-</button>
    </div>
}

现在项目已经可以运行了但是Count组件中的 提交Action的那一长串代码影响视图的可读性,所以代码还是需要优化

计数器案例代码优化-让视图中的代码可读性更高

我们希望视图中直接调用一个函数这样视图代码可读性强,这个需要利用connect的第二个参数,第二个参数是一个函数,这个函数的形参就是dispatch方法,要求这个函数返回一个对象,返回的这个对象中的内容都会映射到组件的props属性上

  1. 申明一个变量为connect中的第二个参数,在这个变量中返回执行不同action操作的对象
// connect 的第二个参数 这个参数是个函数 这个函数的形参就是dispatch方法 要求返回一个对象 这个对象中的属性会被映射到组件的props上
const mapDispatchToProps = dispatch => ({
    increment (){
        dispatch({
            type: 'increment'
        })
    },
    decrement (){
        dispatch({
            type: 'decrement'
        })
    }
})

// connect 的参数为一个函数 这个函数可以拿到store中的状态,要求我们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性
// connect 调用后返回一个函数 返回的这个函数继续调用需要传入组件告诉connect需要映射到那个组件的props
export default connect(mapStateProps, mapDispatchToProps)(Count)
  1. 在组件中结构props在视图中直接绑定事件
function Count({count,increment,decrement}) {
    return <div>
        <button onClick={increment}>+</button>
        <span>{count}</span>
        <button onClick={decrement}>-</button>
    </div>
}

通过这次优化我们发现 调用 dispatch 触发action 的方法的代码都是重复的,所以还需要继续优化

优化调用 dispatch 触发action 的方法的重复代码简化

利用 bindActionCreators 来简化 dispatch 触发 action的操作,bindActionCreators来帮助我们生成执行action动作的函数

bindActionCreators 有两个参数,第一个参数为 执行action的对象,第二个参数为 dispatch方法

  1. 分离action操作,新建store/actions/counter.actions.js文件把执行action操作单独放在这个文件并导出
export const increment = () => ({type: 'increment'})
export const decrement = () => ({type: 'decrement'})
  1. 在Count.js中导入关于计数器的action,用bindActionCreators方法来生成dispatch执行action函数
import { bindActionCreators } from 'redux'
import * as counterActions from './../store/actions/counter.actions'


const mapDispatchToProps = dispatch => (bindActionCreators(counterActions, dispatch))
// connect 的参数为一个函数 这个函数可以拿到store中的状态,要求我们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性
// connect 调用后返回一个函数 返回的这个函数继续调用需要传入组件告诉connect需要映射到那个组件的props
export default connect(mapStateProps, mapDispatchToProps)(Count)

代码优化到这里我们发现,redux的代码与组件融合在一起,所以我需要拆分成独立的,为什么要抽离redux呢?因为我们要让我们的代码结构更加合理

重构计数器,把redux相关代码抽离

把reducer函数抽离为单独的文件、把创建store抽离到单独的文件中

  1. 因为在reducer 和 actions中我们都写了字符串,但是字符串没有提示所以我们把字符串定义成常量防止我们出现单词错误这种低级错误,新建 src/store/const/counter.const.js 文件
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
  1. 新建 src/store/reducers/counter.reducers.js 文件把 reducer 函数抽离到此文件中
import { INCREMENT, DECREMENT} from './../const/counter.const'
const initialState = {
    count: 0
}
// eslint-disable-next-line import/no-anonymous-default-export
export default (state = initialState, action) => {
    switch (action.type) {
        case INCREMENT:
            return {
                count: state.count + 1
            }
        case DECREMENT:
            return {
                count: state.count - 1
            }

        default:
            return state
    }
}
  1. 更改actions中的字符串为引入变量
import { INCREMENT, DECREMENT} from './../const/counter.const'

export const increment = () => ({type: INCREMENT})
export const decrement = () => ({type: DECREMENT})
  1. 创建src/store/index.js文件 ,在这个文件中创建store 并导出
import { createStore } from 'redux';
import reducer from './reducers/counter.reducers'

export const store = createStore(reducer)
  1. 在引入store的文件中改变为冲项目中store文件中引入store
import React from 'react';
import ReactDOM from 'react-dom';
import Count from './components/Count';
import { store } from './store'
import { Provider } from 'react-redux'
/**
 * react-redux 让react 和 redux 完美结合
*    Provider 是一个组件 可以吧创建出来的store 放在一个全局的地方 让组件可以拿到store
*    connect  是一个方法
 */


ReactDOM.render(
  // 通过provider组件 将 store 放在了全局的组件可以够的到的地方  provider要求我们放在最外层组件
  <Provider store={store}><Count /></Provider>,
  document.getElementById('root')
);

为action 传递参数,对计数器案例做扩展

这个计数器案例已经实现了点击按钮加一减一操作了,现在有个新需求我们需要加减一个数值例如加五减五

这就需要对action传递参数了

  1. 在视图中按钮绑定函数传入参数
function Count({count,increment,decrement}) {
    return <div>
        <button onClick={() => increment(5)}>+</button>
        <span>{count}</span>
        <button onClick={() => decrement(5)}>-</button>
    </div>
}
  1. 在dispacth执行action动作时接受参数并传入到action中
export const increment = payload => ({type: INCREMENT, payload})
export const decrement = payload => ({type: DECREMENT, payload})
  1. 在reducers中接收参数并作相应处理
export default (state = initialState, action) => {
    switch (action.type) {
        case INCREMENT:
            return {
                count: state.count + action.payload
            }
        case DECREMENT:
            return {
                count: state.count - action.payload
            }

        default:
            return state
    }
}

原文地址:https://kspf.xyz/archives/10/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在react中使用redux并实现计数器案例 的相关文章

  • 高斯列主消元法 求非齐次线性方程组 C语言实现代码

    高斯列主元素消去法是由高斯消去法改进的算法 下面浅浅分享一下本人对该方法的理解 Ax b 先说高斯消去法 感觉基本的思路就跟我们手算非齐次线性方程组差不多 在线性代数中 我们求解方程组都是这种思路 消元的过程相当于是 由系数矩阵A和非齐次项
  • linux下代码分析工具Splint

    1 C代码静态分析工具 Its4 读取一个或多个 C C 源程序 将每个源程序分割成函数标志流 然后检查生成的标志是否存在于漏洞数据库中 从而得到每个源程序的所有错误警告列表 并带有相关的描 述 其规则库vulns i4d定义了各种函数的危
  • 【医学图像分割】 MIXED Transformer 、DS-TransUNet、Swin-Unet

  • Qt开发北斗定位系统融合百度地图API及Qt程序打包发布

    Qt开发北斗定位系统融合百度地图API及Qt程序打包发布 1 上位机介绍 最近有个接了一个小型项目 内容很简单 就是解析北斗GPS的串口数据然后输出经纬度 但接过来觉得太简单 就发挥了主观能动性 增加了百度地图API 不但能实时定位 还能在
  • 波兰表达式 & 逆波兰表达式

    1 概述 1 1 什么是波兰表达式 先来看看维基百科对于波兰表达式和逆波兰表单的解释 波兰表示法 Polish notation 或波兰记法 是一种逻辑 算术和代数表示方法 其特点是操作符置于操作数的前面 因此也称做前缀表示法 如果操作符的
  • C++ 大话设计之《观察者模式》(优缺点,设计原理,常用场景)

    观察者模式是一种行为型模式 优点 松散耦合 观察者模式提供了一种松散耦合的设计 使得当一个对象的状态发生变化时 它不需要知道其他对象是如何使用这些信息的 这使得系统更容易扩展和维护 动态关联 观察者模式允许在运行时动态地添加或删除观察者 而
  • #bat 利用bat脚本添加/删除环境变量

    目录 添加到Path 从Path中删除 操作环境变量有风险 目标文件夹 current path bin 添加到Path echo path gt gt log txt echo off set pathStr path set mingw
  • 从周赛中学算法-2023上

    从周赛中学算法 2023上 https leetcode cn circle discuss v2RXSN 文章目录 从周赛中学算法 2023上 一 技巧类 2730 找到最长的半重复子字符串 https leetcode cn probl
  • SpringCloudAlibaba之Sentinel 自定义熔断逻辑处理

    Sentinel服务熔断环境搭建 服务熔断 应对微服务雪崩效应的一种链路保护机制 类似保险丝 需要完成Sentinel整合Ribbon openFeign 所以我们先要搭建环境 那么先从整合Ribbon开始 环境搭建 为了演示操作 所以在这
  • 手把手教你上手Apache DolphinScheduler机器学习工作流

    摘要 Apache DolphinScheduler 3 1 0发版后 添加了诸多AI组件 帮助用户在Apache DolphinScheduler上更方便地构建机器学习工作流 本文介绍如何建立DolphinScheduler与一些机器学习
  • Windows中如何查看日志(如查看远程登陆的IP地址)以及常用日志ID

    时间 2018 12 12 题目 Windows中如何查看日志 如查看远程登陆的IP地址 以及常用日志ID 概述 在Windows中可以使用 事件查看器 来查看相关日志 并结合日志ID进行日志筛选 常见的日志有 4634 帐户被注销 464
  • 将SSE指令转换为ARM NEON指令

    相关资料 sse指令集 sse指令解释 sse2neon仓库 可以在sse2neon h中寻找对应的neon指令转换方法 注意事项 将sse指令转换为arm neon指令往往很难起到优化作用 甚至可能产生负优化 因此该部分优化仅供参考 mm
  • 12.计算机网络---iptables防火墙管理工具

    文章目录 一 防火墙基础知识 1 1 防火墙是什么 1 2 iptables基础知识 1 3 netfilter和iptables的关系 1 4 新型防火墙工具 firewalld 二 iptables的四表五链 2 1 规则表 2 2 规
  • Python爬虫之使用MongoDB存储数据

    1 MongoDB的安装 MongoDB是一种非关系型数据库 MongoDB官网 选择你的系统对应的版本下载安装即可 2 MongoDB配置 a 在C盘或者D盘建一个文件夹如图mongodb b 安装成功后里面会有bin文件然后再文件夹里面
  • sudo配置文件/etc/sudoers深入介绍

    简介 sudo命令对应的用户权限授权配置文件为 etc sudoers 我们可以使用专用工具visudo来完成有关sudo的授权管理配置 使用visudo工具的好处是在添加规则之后 保存退出时会检查授权配置的语法 这一点很重要 曾经有人直接
  • 理解矩阵 from孟岩--流星小屋

    理解矩阵 from孟岩 前不久chensh出于不可告人的目的 要充当老师 教别人线性代数 于是我被揪住就线性代数中一些务虚性的问题与他讨论了几次 很明显 chensh觉得 要让自己在讲线性代数的时候不被那位强势的学生认为是神经病 还是比较难
  • vscode 用git 拉取代码,提示:在签出前,请清理存储库工作树。请问是什么问题,如何解决

    问题主要是git仓库上的代码和本地代码存在冲突 解决办法 1 新建一个文件夹重新从git拉取最新的代码 使用beyond compare对比合并自己修改的代码到新拉的代码里 提交 2 放弃本地修改 直接覆盖 git reset hard g
  • Hadoop分布式文件系统(HDFS)Java接口(HDFS Java API)详细版

    误用聪明 何若一生守拙 滥交朋友 不如终日读书 相关连接 HDFS相关知识 Hadoop分布式文件系统 HDFS 快速入门 Hadoop分布式文件系统 HDFS 知识梳理 超详细 Hadoop集群连接 Eclipse连接Hadoop集群 I
  • Vant UI使用iconfont自定义图标

    在使用Vant UI做h5页面时 不可避免的会使用到各种小图标 但是Vant 官方提供的图标是有限的 考虑到这种情况 vant也提供了一种方法去自定义图标 自定义图标 可能有些同学看到这里也是一头雾水 下面有详细的教程 iconfont 让
  • 栈的逆序

    题目描述 实现一个栈的逆序 但是只能用递归函数和这个栈本身的pop操作来实现 而不能自己申请另外的数据结构 给定一个整数数组A即为给定的栈 同时给定它的大小n 请返回逆序后的栈 测试用例 输入 4 3 2 1 4 输出 1 2 3 4 解题

随机推荐