在react中使用redux并实现计数器案例
React + Redux
在recat中不使用redux 时遇到的问题
在react中组件通信的数据是单向的,顶层组件可以通过props属性向下层组件传递数据,而下层组件不能向上层组件传递数据,要实现下层组件修改数据,需要上层组传递修改数据的方法到下层组件,当项目越来越的时候,组件之间传递数据变得越来越困难

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

使用redux
下载redux
npm install redux react-redux
redux 工作流程
- 组件通过 dispatch 触发action
- store 接受 action 并将 action 分发给 reducer
- reducer 根据 action 类型对状态进行更改并将更改后的数据返回给store
- 组件订阅了store中的状态,store中的状态更新会同步到组件

使用react+redux实现计数器
- 创建项目,并安装 redux
# 如果没有安装react脚手架则执行这条命令安装reate脚手架
npm install -g create-react-app
# 创建reate项目
create-react-app 项目名
# 进入项目
cd 项目名
# 安装 redux
npm install redux reate-redux
- 引入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
}
}
在react中使用redux并实现计数器案例的更多相关文章
- react系列(五)在React中使用Redux
上一篇展示了Redux的基本使用,可以看到Redux非常简单易用,不限于React,也可以在Angular.Vue等框架中使用,只要需要Redux的设计思想的地方,就可以使用它. 这篇主要讲解在Rea ...
- 在React中使用Redux
这是Webpack+React系列配置过程记录的第六篇.其他内容请参考: 第一篇:使用webpack.babel.react.antdesign配置单页面应用开发环境 第二篇:使用react-rout ...
- react中使用redux简易案例讲解
为什么我想要使用redux? 前段时间初步上手了react,最近在使用react的过程中发现对于组件之间通信的需求比较迫切,尤其是在axios异步请求后端数据的时候,这样的需求是特别强烈的!举个例子: ...
- react中对于redux的封装
const createStore = (reducer)=>{ //默认的state对象 let state = {}; //将所有订阅的事件存在在这个数组中 let listeners = ...
- 在React中使用Redux数据流
问题:数据流是什么呢?为什么要用数据流? 答案:1.数据流是我们的行为与相应的抽象 2.使用数据流帮助我们明确了行为的对应的响应 问题: React与数据流的关系 1.React是纯 V 层的前端框架 ...
- 如何在非 React 项目中使用 Redux
本文作者:胡子大哈 原文链接:https://scriptoj.com/topic/178/如何在非-react-项目中使用-redux 转载请注明出处,保留原文链接和作者信息. 目录 1.前言 2. ...
- 如何优雅地在React项目中使用Redux
前言 或许你当前的项目还没有到应用Redux的程度,但提前了解一下也没有坏处,本文不会安利大家使用Redux 概念 首先我们会用到哪些框架和工具呢? React UI框架 Redux 状态管理工具,与 ...
- 优雅的在React项目中使用Redux
概念 首先我们会用到哪些框架和工具呢? React UI框架 Redux 状态管理工具,与React没有任何关系,其他UI框架也可以使用Redux react-redux React插件,作用:方便在 ...
- react中界面跳转 A界面跳B界面,返回A界面,A界面状态保持不变 redux的state方法
在上一篇文章中说过了react中界面A跳到B,返回A,A界面状态保持不变,上篇中使用的是传统的localStorage方法,现在来使用第二种redux的state方法来实现这个功能 现在我刚接触red ...
随机推荐
- Salesforce学习之路(六)利用Visualforce Page实现页面的动态刷新功能
Visualforce是一个Web开发框架,允许开发人员构建可以在Lightning平台上本地托管的自定义用户界面.其框架包含:前端的界面设计,使用的类似于HTML的标记语言:以及后端的控制器,使用类 ...
- [源码分析] 分布式任务队列 Celery 多线程模型 之 子进程
[源码分析] 分布式任务队列 Celery 多线程模型 之 子进程 目录 [源码分析] 分布式任务队列 Celery 多线程模型 之 子进程 0x00 摘要 0x01 前文回顾 1.1 基类作用 1. ...
- java面试一日一题:讲下redo log
问题:请讲下redo log的作用 分析:mysql中有很多日志,例,binlog undo log redo log,要弄清楚这些日志的作用,就要了解这些日志出现的背景及要解决的问题? 回答要点: ...
- 1438. Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit
Given an array of integers nums and an integer limit, return the size of the longest continuous suba ...
- Vulkan移植GpuImage(四)从D到O的滤镜
现把D到O的大部分滤镜用vulkan的ComputeShader实现了,列举其中一些有点特殊的说明. GaussianBlurPosition 指定区域高斯模糊 没有按照GPUImage里的方式实现, ...
- 1-OSI七层模型
网络功能:数据传输 ISO(国际标准化组织) OSI七层模型---->网络通信工作流程的标准化 OSI七层模型 应用层:提供用户服务,具体功能由特定的程序而定. 表示层:数据的压缩优化,加密. ...
- Docker怎么实现容器之间的通信?
//TODO 参考资料: https://developer.aliyun.com/article/55912 https://blog.csdn.net/u011541946/article/det ...
- 【JVM】空间分配担保机制
抛几个问题: 1.谁进行空间担保? JVM使用分代收集算法,将堆内存划分为年轻代和老年代,两块内存分别采用不同的垃圾回收算法,空间担保指的是老年代进行空间分配担保 2.什么是空间分配担保? 在发生Mi ...
- 【JDK8】Java8 新增BASE64加解密API
什么是Base64编码? Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法 基于64个字符A-Z,a-z,0-9,+,/ ...
- Linux运维比较常用的一些脚本
目录 一.根据PID过滤进程所有信息 二.根据进程名过滤进程信息 三.根据用户名查询该用户的相关信息 四.加固系统的一些配置 五:实现磁盘分区的 六.使用一整块硬盘创建逻辑卷 七.将一块硬盘分区,然后 ...