使用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 相关文章推荐
对xmlHttp对象方法和属性的理解
Jan 17 Javascript
文本框只能选择数据到文本框禁止手动输入
Nov 22 Javascript
页面元素绑定jquery toggle后元素隐藏的解决方法
Mar 27 Javascript
原生js的弹出层且其内的窗口居中
May 14 Javascript
使用jQuery UI库开发Web界面的简单入门指引
Apr 22 Javascript
详解Vue 非父子组件通信方法(非Vuex)
May 24 Javascript
基于百度地图api清除指定覆盖物(Overlay)的方法
Jan 26 Javascript
在vue2.0中引用element-ui组件库的方法
Jun 21 Javascript
Vue 组件封装 并使用 NPM 发布的教程
Sep 30 Javascript
vue 中Virtual Dom被创建的方法
Apr 15 Javascript
JS页面获取 session 值,作用域和闭包学习笔记
Oct 16 Javascript
解决VUE mounted 钩子函数执行时 img 未加载导致页面布局的问题
Jul 27 Javascript
微信小程序基础教程之echart的使用
vue实现水波涟漪效果的点击反馈指令
vue 实现上传组件
May 31 #Vue.js
vue基于Teleport实现Modal组件
Vue+Element UI实现概要小弹窗的全过程
vue-cli4.5.x快速搭建项目
Vue CLI中模式与环境变量的深入详解
You might like
用PHP伪造referer突破网盘禁止外连的代码
2008/06/15 PHP
用PHP将网址字符串转换成超链接(网址或email)
2010/05/25 PHP
php实现下载限制速度示例分享
2014/02/13 PHP
php的hash算法介绍
2014/02/13 PHP
php中smarty实现多模版网站的方法
2015/06/11 PHP
针对PHP开发安全问题的相关总结
2019/03/22 PHP
JSON扫盲帖 JSON.as类教程
2009/02/16 Javascript
让 JavaScript 轻松支持函数重载 (Part 2 - 实现)
2009/08/04 Javascript
JQuery中如何传递参数如click(),change()等具体实现
2013/04/28 Javascript
jQuery jcrop插件截图使用方法
2013/11/20 Javascript
jquery创建表格(自动增加表格)代码分享
2013/12/25 Javascript
javascript实现的元素拖动函数宿主为浏览器
2014/07/21 Javascript
Angular的事件和表单详解
2016/12/26 Javascript
loading动画特效小结
2017/01/22 Javascript
bootstarp modal框居中显示的实现代码
2017/02/18 Javascript
JS实现线性表的链式表示方法示例【经典数据结构】
2017/04/11 Javascript
VUE axios上传图片到七牛的实例代码
2017/07/28 Javascript
vue弹窗消息组件的使用方法
2020/09/24 Javascript
vue props传值失败 输出undefined的解决方法
2018/09/11 Javascript
node+express框架中连接使用mysql(经验总结)
2018/11/10 Javascript
vue项目中常见问题及解决方案(推荐)
2019/10/21 Javascript
Node.js利用Express实现用户注册登陆功能(推荐)
2020/10/26 Javascript
Python 模板引擎的注入问题分析
2017/01/01 Python
Python变量和数据类型详解
2017/02/15 Python
Python网络爬虫中的同步与异步示例详解
2018/02/03 Python
python 实现查找文件并输出满足某一条件的数据项方法
2019/06/12 Python
详解WebSocket跨域问题解决
2018/08/06 HTML / CSS
网购亚洲时装、美容产品和生活百货:YesStyle
2016/09/15 全球购物
Shopee印度尼西亚:东南亚与台湾市场最大电商平台
2018/06/17 全球购物
测绘工程个人的自我评价
2013/11/10 职场文书
个人承诺书怎么写
2014/05/24 职场文书
征用土地赔偿协议书
2014/09/26 职场文书
刑事附带民事起诉状
2015/05/19 职场文书
幼儿园中班教学反思
2016/03/03 职场文书
Redis安装启动及常见数据类型
2021/04/14 Redis
Golang 实现超大文件读取的两种方法
2021/04/27 Golang