使用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之文件操作
Mar 07 Javascript
判断对象是否Window的实现代码
Jan 10 Javascript
14个有用的Jquery技巧分享
Jan 08 Javascript
jQuery实现点击按钮文字变成input框点击保存变成文字
May 09 Javascript
深入浅出讲解ES6的解构
Aug 03 Javascript
使用JS动态显示文本
Sep 09 Javascript
微信小程序云开发实现数据添加、查询和分页
May 17 Javascript
基于Vue el-autocomplete 实现类似百度搜索框功能
Oct 25 Javascript
js实现多个标题吸顶效果
Jan 08 Javascript
40行代码把Vue3的响应式集成进React做状态管理
May 20 Javascript
vue设置默认首页的操作
Aug 12 Javascript
vue绑定class的三种方法
Dec 24 Vue.js
微信小程序基础教程之echart的使用
vue实现水波涟漪效果的点击反馈指令
vue 实现上传组件
May 31 #Vue.js
vue基于Teleport实现Modal组件
Vue+Element UI实现概要小弹窗的全过程
vue-cli4.5.x快速搭建项目
Vue CLI中模式与环境变量的深入详解
You might like
使用无限生命期Session的方法
2006/10/09 PHP
php简单判断两个字符串是否相等的方法
2015/07/13 PHP
详解Laravel视图间共享数据与视图Composer
2016/08/04 PHP
PHP使用PDO实现mysql防注入功能详解
2019/12/20 PHP
fckeditor 获取文本框值的实现代码
2009/02/09 Javascript
javascript 读取图片文件的大小
2009/06/25 Javascript
一个收集图片的bookmarlet(js 刷新页面中的图片)
2010/05/27 Javascript
js+数组实现网页上显示时间/星期几的实用方法
2013/01/18 Javascript
JavaScript避免内存泄露及内存管理技巧
2014/09/05 Javascript
15个jquery常用方法、小技巧分享
2015/01/13 Javascript
jQuery实现仿美橙互联两级导航菜单的方法
2015/03/09 Javascript
jQuery使用$.get()方法从服务器文件载入数据实例
2015/03/25 Javascript
JavaScript输出当前时间Unix时间戳的方法
2015/04/06 Javascript
jQuery插件MixItUp实现动画过滤和排序
2015/04/12 Javascript
JavaScript forEach()遍历函数使用及介绍
2015/07/08 Javascript
JavaScript数组对象赋值用法实例
2015/08/04 Javascript
详解JavaScript基本类型和引用类型
2015/12/09 Javascript
获取JavaScript异步函数的返回值
2016/12/21 Javascript
js记录点击某个按钮的次数-刷新次数为初始状态的实例
2017/02/15 Javascript
jQuery插件HighCharts绘制简单2D折线图效果示例【附demo源码】
2017/03/21 jQuery
js 开发之autocomplete=&quot;off&quot;在chrom中失效的解决办法
2017/09/28 Javascript
vue中动态绑定表单元素的属性方法
2018/02/23 Javascript
Javascript中parseInt的正确使用方式
2018/10/17 Javascript
Python最基本的数据类型以及对元组的介绍
2015/04/14 Python
Python使用chardet判断字符编码
2015/05/09 Python
Python正则表达式使用经典实例
2016/06/21 Python
Python判断变量是否为Json格式的字符串示例
2017/05/03 Python
python中format()函数的简单使用教程
2018/03/14 Python
python numpy 反转 reverse示例
2019/12/04 Python
世界上最大的冷却器制造商:Igloo Coolers
2019/07/23 全球购物
彩色的翅膀教学反思
2014/04/25 职场文书
2014年综治宣传月活动总结
2014/04/28 职场文书
公司担保书范文
2014/05/21 职场文书
六一儿童节新闻稿
2015/07/17 职场文书
MySQL sql_mode的使用详解
2021/05/08 MySQL
多人盗宝《绿林侠盗》第三赛季4.5上线 跨平台实装
2022/04/03 其他游戏