使用react+redux实现计数器功能及遇到问题


Posted in Javascript onJune 02, 2021

Redux,本身就是一个单纯的状态管理者,我们不追溯它的历史,从使用角度来说:它提供一个全局的对象store,store中包含state对象用以包含所有应用数据,并且store提供了一些reducer方法。这些方法可以自定义,使用调用者得以改变state的值。state的值仅为只读,如果需要更改则必须只能通过reducer。

Redux

  • 核心对象:store
  • 数据存储:state
  • 状态更新提交接口:==dispatch==
  • 状态更新提交参数:带type和payload的==Action==
  • 状态更新计算:==reducer==
  • 限制:reducer必须是纯函数,不支持异步
  • 特性:支持中间件

React + Redux

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

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

使用react+redux实现计数器功能及遇到问题

在react中加入redux 的好处

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

使用react+redux实现计数器功能及遇到问题

使用redux

下载redux

npm install redux react-redux

redux 工作流程

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

使用react+redux实现计数器功能及遇到问题

使用react+redux实现计数器

1.创建项目,并安装 redux

# 如果没有安装react脚手架则执行这条命令安装reate脚手架
npm install -g create-react-app
# 创建reate项目
create-react-app 项目名
# 进入项目
cd 项目名
# 安装 redux
npm install redux reate-redux

2.引入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

新建 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

引入 Provider 组件放置在最外层,并制定store

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

引入 connect 方法 根据 connect 的使用来包裹组件

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

改造 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属性上

申明一个变量为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)

在组件中结构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方法

分离action操作,新建store/actions/counter.actions.js文件把执行action操作单独放在这个文件并导出

export const increment = () => ({type: 'increment'})
export const decrement = () => ({type: 'decrement'})

在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抽离到单独的文件中

因为在reducer 和 actions中我们都写了字符串,但是字符串没有提示所以我们把字符串定义成常量防止我们出现单词错误这种低级错误,新建 src/store/const/counter.const.js 文件

export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

新建 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
    }
}

更改actions中的字符串为引入变量

import { INCREMENT, DECREMENT} from './../const/counter.const'

export const increment = () => ({type: INCREMENT})
export const decrement = () => ({type: DECREMENT})

创建src/store/index.js文件 ,在这个文件中创建store 并导出

import { createStore } from 'redux';
import reducer from './reducers/counter.reducers'

export const store = createStore(reducer)

在引入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传递参数了

在视图中按钮绑定函数传入参数

function Count({count,increment,decrement}) {
    return <div>
        <button onClick={() => increment(5)}>+</button>
        <span>{count}</span>
        <button onClick={() => decrement(5)}>-</button>
    </div>
}

在dispacth执行action动作时接受参数并传入到action中

export const increment = payload => ({type: INCREMENT, payload})
export const decrement = payload => ({type: DECREMENT, payload})

在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/

到此这篇关于在react中使用redux并实现计数器案例的文章就介绍到这了,更多相关react redux实现计数器内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
如何确保JavaScript的执行顺序 之jQuery.html并非万能钥匙
Mar 03 Javascript
Javascript倒计时页面跳转实例小结
Sep 11 Javascript
如何让你的Lightbox支持滚轮缩放及Base64图片
Dec 04 Javascript
简介JavaScript中Math.cos()余弦方法的使用
Jun 15 Javascript
JS把内容动态插入到DIV的实现方法
Jul 19 Javascript
JavaScript简单下拉菜单特效
Sep 13 Javascript
js实现HashTable(哈希表)的实例分析
Nov 21 Javascript
JavaScript中for循环的几种写法与效率总结
Feb 03 Javascript
js获取浏览器和屏幕的各种宽度高度
Feb 22 Javascript
js实现二级导航功能
Mar 03 Javascript
node.js中实现kindEditor图片上传功能的方法教程
Apr 26 Javascript
Vue事件处理原理及过程详解
Mar 11 Javascript
微信小程序基础教程之echart的使用
vue实现水波涟漪效果的点击反馈指令
vue 实现上传组件
May 31 #Vue.js
vue基于Teleport实现Modal组件
Vue+Element UI实现概要小弹窗的全过程
vue-cli4.5.x快速搭建项目
Vue CLI中模式与环境变量的深入详解
You might like
php防止伪造数据从地址栏URL提交的方法
2014/08/24 PHP
PHP实现深度优先搜索算法(DFS,Depth First Search)详解
2017/09/16 PHP
sina的lightbox效果。
2007/01/09 Javascript
js类的静态属性和实例属性的理解
2009/10/01 Javascript
JavaScript 面向对象编程(2) 定义类
2010/05/18 Javascript
jquery选择器需要注意的问题
2014/11/26 Javascript
jQuery链使用指南
2015/01/20 Javascript
WordPress中鼠标悬停显示和隐藏评论及引用按钮的实现
2016/01/12 Javascript
原生js实现中奖信息无间隙滚动效果
2017/01/18 Javascript
详解nodeJs文件系统(fs)与流(stream)
2018/01/24 NodeJs
angular 未登录状态拦截路由跳转的方法
2018/10/09 Javascript
vue 实现动态路由的方法
2020/07/06 Javascript
vue 限制input只能输入正数的操作
2020/08/05 Javascript
详解vue3中组件的非兼容变更
2021/03/03 Vue.js
[02:53]DOTA2英雄基础教程 山岭巨人小小
2013/12/09 DOTA
[55:03]完美世界DOTA2联赛PWL S2 LBZS vs FTD.C 第二场 11.20
2020/11/20 DOTA
Python学习资料
2007/02/08 Python
python代码检查工具pylint 让你的python更规范
2012/09/05 Python
Python装饰器decorator用法实例
2014/11/10 Python
Python列表计数及插入实例
2014/12/17 Python
Python的条件语句与运算符优先级详解
2015/10/13 Python
Django基于ORM操作数据库的方法详解
2018/03/27 Python
python 不同方式读取文件速度不同的实例
2018/11/09 Python
python替换字符串中的子串图文步骤
2019/06/19 Python
python对绑定事件的鼠标、按键的判断实例
2019/07/17 Python
Python定时任务工具之APScheduler使用方式
2019/07/24 Python
Django对models里的objects的使用详解
2019/08/17 Python
Flask框架路由和视图用法实例分析
2019/11/07 Python
Python基于smtplib模块发送邮件代码实例
2020/05/29 Python
python实现图像随机裁剪的示例代码
2020/12/10 Python
英国家喻户晓的高街品牌:River Island
2017/11/28 全球购物
英国伦敦的睡衣品牌:Asceno
2019/10/06 全球购物
资深地理教师自我评价
2013/09/21 职场文书
物流管理毕业生自荐信
2013/10/24 职场文书
毕业生求职自荐书范文
2014/03/27 职场文书
写求职信有哪些注意事项
2014/05/08 职场文书