壹 ❀ 引

在前面两篇文章中,我们介绍了reduxcontext部分概念与基本用法,这里我们做个简单复习。

redux属于应用数据流框架,主要用于应用状态的管理,比如react中的state。其数据流为view-->action-->reducer-->store-->view,比如用户点击了一个按钮,本质触发的是store.dispatch(action),然后reducer感知事件,触发actionType对应的更新数据方法,从而达到更新store的目的,而当store更新后,早在view订阅的store.subscribe又被触发,这样又通过store.getState拿到最新的store并将其设置为组件的statestate发生变化自然会让组件重新render,这便是一次完整的数据更新过程。

而随之问题也暴露出来了,多个组件需要使用store的数据都得引入store.js文件,而且dispatch以及subscribe等API都在store上,所以你需要使用这些方法的地方也一样得提前引入store。于是,我们紧接着介绍了context概念,context(上下文)的作用主要用于解决组件跨级传值问题,比如上面提到的API方法,我们就可以通过context.Providervaluestore传递下去,这样不管哪个组件都可以通过类似this.context.dispatch的写法调用store上的方法。除了全局传递外,比如A-->B-->C-->D场景,D需要拿到A的数据,但是又不希望BC作为数据传递的工具人,使用context.Consumer同样能解决这一类场景的问题。

接下来要介绍的react-reduxredux作者为react量身定制的库,使用上进一步简化了我们对于redux以及context的写法。由于react-redux是独立redux的存在,因此我们需要单独引入它。在项目根目录执行npm install --save react-redux,接下来我们来了解其在用法的变化,没关系,有前两篇文章的铺垫,这并不会很难!本文开始。

贰 ❀ react-redux

react-redux提供了ProviderconnectmapStateToPropsmapDispatchToProps四个API,我们来一一介绍它们。

贰 ❀ 壹 Provider

在之前context中我们使用Provider得先通过React.createContext()创建,不过有了react-redux后我们可以直接引用,比如:

import { Provider } from 'react-redux'

其用法与含义与之前完全相同,我们还是使用之前文章的例子,在index中做部分修改,将Provider的引用改为react-redux

import React, { Component } from 'react';
import { Provider } from 'react-redux'
import ReactDOM from 'react-dom';
import store from './Store.js';
import Counter from './Counter.js';
import Summary from './Summary.js';
class ControlPanel extends Component {
render() {
return (
<div>
<Counter caption="First" />
<Counter caption="Second" />
<hr />
<Summary />
</div>
);
}
}
ReactDOM.render(
<Provider value={store}>
<ControlPanel />
</Provider>,
document.getElementById('root')
);

注意,这次我们把Provider用在了ReactDOM.render中,也就是进行了真正意义上的全局包裹,之前文章的例子由于ControlPanel组件自身没有使用到store的数据,而是它的子组件需要用,所以之前的写法如下,也没有什么问题:

class ControlPanel extends Component {
render() {
return (
//我们使用了Provider包裹子组件,通过value传递store
<context.Provider value={store}>
<div>
<Counter caption="First" />
<Counter caption="Second" />
<hr />
<Summary />
</div>
</context.Provider>
);
}
}
ReactDOM.render(
<ControlPanel />,
document.getElementById('root')
);

这里只是做个写法纠正说明,并不是react-redux特性如此必须这么写,所以单独做个说明。保存后运行项目会报错,毕竟后续还有代码还没改完,我们先这样。

贰 ❀ 贰 connect

上面的代码修改,我们为全局上下文中添加了数据store,还记得在之前context介绍中如何使用上下文中的数据吗?两种方式,第一种是通过conponentName.contextType = context先为当前组件绑定上下文,之后在constructor中通过super(context)引入,之后就可以通过this.context访问上下文了,第二种方式通过Consumer中回调函数形参直接访问。

而在react-redux中我们通过connect方法帮助组件链接全局上下文,比如:

import { connect } from 'react-redux';
export default connect(mapStateToProps, mapDispatchToProps)(component);

上面代码中component就是你当前创建的组件,由于这个组件没有与上下文扯上关系,所以这里我们使用connect帮助它链接全局store,其次connect接受两个参数,分别是mapStateToPropsmapDispatchToProps,有什么用我们后面再介绍。

这里我们补充一个概念,react-redux将组件分为UI组件容器组件UI组件很好理解,只负责view渲染,不管理state变化,不管理业务逻辑,也不使用redux的API,它所接受的一切数据方法均由props提供。

而所谓容器组件作用与UI组件互补,它的工作是负责state变更以及业务逻辑处理,而这里的容器组件其实由react-redux生成。

注意上面connect这行代码,它本质上等同于:

// 帮UI组件注入数据方法,于是得到了一个新的容器组件,connect就像给一部手机通了电一样,让其有了生命力
const 容器组件 = connect(mapStateToProps, mapDispatchToProps)(UI组件);
export 容器组件;

也就是说我们通过connect帮助一个UI组件链接到了数据层,这里的数据可以是全局的store,也可以是上层组件传递下来的props。于是我们得到了一个被注入了数据的新组件。

说到这里,不知道大家能不能感受到这种做法与context使用全局store的差异性,context使用数据要么直接把上下文与组件绑定后使用,要么借用Consumer使用,就像被内嵌进组件一样,耦合度较高。而connect更像在组件外部给其开了一个传递数据的入口,不管你数据哪里来的,都通过我这里传递进入,方式上就比较统一了,无论是全局store还是上次的props,都可以通过这里以props的方式统一注入,那么组件内部呢都给我通过props的方式访问外部传递进来的方法或者数据。

当然,上述关于UI组件的分类说明比较严格了,实际项目开发中也存在很多包含了业务代码以及自身state的组件使用connect链接store的做法。这里只是科普UI组件容器组件的概念,有时候硬要将一个组件抽离成两个组件反而是一件麻烦事,具体看大家习惯。

上面说了connect用于帮助组件链接数据,那么具体做呢?其实就得依赖上面提到的两个参数了,我们接着说。

贰 ❀ 叁 mapStateToProps

顾名思义,建立stateprops的映射,mapStateToProps接受2个参数,比如:

const mapStateToProps = (state,ownProps) => {
return {
name:state.name,
age:ownProps.age
}
}

说直白点,比如从全局的storestore本身可以理解为全局的state)以及外层组件传递给你的props中提取并组合成当前组件所需要的数据。

上面的代码中返回了一个新的对象,这个对象包含了一个name一个age,数据来源分别是全局state与外层组件传递的props,那么当前组件通过this.props即可访问到这两个属性,而且,无论是外部的state还是外部传递的ownProps发生了变化,都会再次触发此方法(如果你没用这两个参数就不会触发,毕竟没有依赖关系),目的是同步更新传递当前组件的props

贰 ❀ 肆 mapDispatchToProps

看到方法名中的dispatch,直觉应该想到此方法应该跟store.dispatch有关。没错,我们在学习context时,凡是组件需要派发action时最终调用的都是this.context.dispatch,写法上多少有些繁琐。mapDispatchToProps的作用,其实也是将dispatch行为抽离了出去,然后也作为props一部分传进了组件,此方法接受两个参数,如下:

function mapDispatchToProps(dispatch, ownProps) {
return {
onIncrement: () => {
dispatch(Actions.increment(ownProps.caption));
},
}
}

dispatch作用其实与store.dispatch作用相同,只是react-redux对齐进行了封装,我们不用再借用this.context.dispatch调用,而是可以直接在mapDispatchToProps方法中使用,是不是用法上便捷了很多呢?其次,mapDispatchToProps返回了一个对象,包含了一个方法,那么在当前组件内部,调用this.props.onIncrement本质上其实就是在做派发。

ownPropsmapStateToProps的第二个参数作用相同,都是外层组件组件传递的props

叁 ❀ 一个例子

OK,我们介绍了react-redux带来的新概念,让我们使用这些API改写上一篇文章中的例子,近距离感受它们所带来的便捷性。在前面我们其实已经完成了对index.js的改写,接下来要做的是对Counter组件与Summary组件的改写,先上Counter.js的代码:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import * as Actions from './Actions.js';
import { connect } from 'react-redux'
class Counter extends Component {
render() {
const { onIncrement, onDecrement, caption, value } = this.props;
return (
<div>
<button onClick={onIncrement}>+</button>
<button onClick={onDecrement}>-</button>
<span>{caption} count: {value}</span>
</div>
);
}
} Counter.propTypes = {
caption: PropTypes.string.isRequired,
onIncrement: PropTypes.func.isRequired,
onDecrement: PropTypes.func.isRequired,
value: PropTypes.number.isRequired
};
// 用于将`store`的数据加工成当前组件所需要的数据
function mapStateToProps(state, ownProps) {
console.log(state);
console.log(ownProps);
return {
value: state[ownProps.caption]
}
}
// dispatch的操作都被提到这里了,组件瞬间就干净了
function mapDispatchToProps(dispatch, ownProps) {
return {
onIncrement: () => {
dispatch(Actions.increment(ownProps.caption));
},
onDecrement: () => {
dispatch(Actions.decrement(ownProps.caption));
}
}
} export default connect(mapStateToProps, mapDispatchToProps)(Counter);

这里我们尝试打印mapStateToProps两个参数,你会发现其实state就是全局storeownProps就是父级组件传下来的props

你会发现,Counter组件活生生被抽离成了一个UI组件,以前我们还在组件中定义包裹dispatch的方法,定义初始化state的方法,还需要添加subscribe订阅store的变化,但现在,store的变化感知以及组件state的初始化都交给了mapStateToProps来完成,前面说了,只要外层state变化,都会触发此方法重新渲染。

同理,对于dispatch方法的定义我们也交给了mapDispatchToProps来完成,而组件内部只需要通过this.props.onIncrement就能执行action派发,是不是相对于之前的写法,简洁了很多呢?

同理,我们修改Summary组件,代码如下:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux'
class Summary extends Component {
render() {
const {sum} = this.props;
return (
<div>Total Count: {sum}</div>
);
}
} Summary.propTypes = {
sum: PropTypes.number.isRequired
}; function mapStateToProps(state) {
let sum = 0;
for (const key in state) {
if (state.hasOwnProperty(key)) {
sum += state[key];
}
}
return {sum};
} export default connect(mapStateToProps)(Summary);

保存代码,这个小例子又运行起来,相较于redux结合context的写法,react-redux确实很大程度上精简了代码量。

肆 ❀ 总

那么到这里,我们通过react-redux再次改写了之前的例子,通过这篇文章,我们知道react-redux与传统redux的差异性,从写代码的角度,不得不说react-redux更加的精简与方便。当然,redux其实还有不少进阶知识我们还未提及,比如中间件,比如异步处理,再或者对于reducer的合并等等,这些知识在后面的文章我们会慢慢介绍,那么本文到此结束。

参考

Redux 中文文档

Redux 入门教程(三):React-Redux 的用法

深入浅出React和Redux第三章

从零开始的react入门教程(十),快速上手react-redux,相对于redux它究竟简化了什么?的更多相关文章

  1. [转] Redux入门教程(快速上手)

    学习前提 在我们开始以前,确保你熟悉以下知识: 函数式JavaScript 面向对象JavaScript JavaScript ES6 语法 同时,确保你的设备已经安装: NodeJS Yarn(或者 ...

  2. [译]:Xamarin.Android开发入门——Hello,Android快速上手

    返回索引目录 原文链接:Hello, Android_Quickstart. 译文链接:Xamarin.Android开发入门--Hello,Android快速上手 本部分介绍利用Xamarin开发A ...

  3. react 入门教程 阮一峰老师真的是榜样

    -  转自阮一峰老师博客 React 入门实例教程   作者: 阮一峰 日期: 2015年3月31日 现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Nati ...

  4. React入门教程1---初见面

    React入门教程1---初见面:https://blog.csdn.net/solar_lan/article/details/82799248 React 教程 React 是一个用于构建用户界面 ...

  5. 无废话ExtJs 入门教程十九[API的使用]

    无废话ExtJs 入门教程十九[API的使用] extjs技术交流,欢迎加群(201926085) 首先解释什么是 API 来自百度百科的官方解释:API(Application Programmin ...

  6. 无废话ExtJs 入门教程十六[页面布局:Layout]

    无废话ExtJs 入门教程十六[页面布局:Layout] extjs技术交流,欢迎加群(201926085) 首先解释什么是布局: 来自百度词典的官方解释:◎ 布局 bùjú: [distributi ...

  7. 无废话ExtJs 入门教程十五[员工信息表Demo:AddUser]

    无废话ExtJs 入门教程十五[员工信息表Demo:AddUser] extjs技术交流,欢迎加群(201926085) 前面我们共介绍过10种表单组件,这些组件是我们在开发过程中最经常用到的,所以一 ...

  8. 无废话ExtJs 入门教程十四[文本编辑器:Editor]

    无废话ExtJs 入门教程十四[文本编辑器:Editor] extjs技术交流,欢迎加群(201926085) ExtJs自带的编辑器没有图片上传的功能,大部分时候能够满足我们的需要. 但有时候这个功 ...

  9. 无废话ExtJs 入门教程十二[下拉列表联动:Combobox_Two]

    无废话ExtJs 入门教程十二[下拉列表联动:Combobox_Two] extjs技术交流,欢迎加群(201926085) 不管是几级下拉列表的联动实现本质上都是根据某个下拉列表的变化,去动态加载其 ...

  10. 无废话ExtJs 入门教程十[单选组:RadioGroup、复选组:CheckBoxGroup]

    无废话ExtJs 入门教程十[单选组:RadioGroup.复选组:CheckBoxGroup] extjs技术交流,欢迎加群(201926085) 继上一节内容,我们在表单里加了个一个单选组,一个复 ...

随机推荐

  1. influxdb 端点使用http进行sql查询,写数据

    转载请注明出处: InfluxDB有以下几个常用的端点,它们的作用和传参方式如下: 1./ping 端点: 作用:用于检查InfluxDB实例的状态,返回InfluxDB的构建类型和版本信息. 传参: ...

  2. 如何部署两个JMS网关,形成双机热备

    大家使用JMS的过程中,可能会留意到,不管是微服务在注册时,还是RemoteClient构造时,所指向的网关都是一个NetAddress数组,之所以网关地址是多个,而不是一个,那是因为网关是一个双击热 ...

  3. 3 分钟了解 NVIDIA 新出的 H200

    英伟达在 2023 年全球超算大会上发布了备受瞩目的新一代 AI 芯片--H200 Tensor Core GPU.相较于上一代产品 H100,H200 在性能上实现了近一倍的提升,内存容量翻倍,带宽 ...

  4. [转帖]Prometheus 监控之 Blackbox_exporter黑盒监测 [icmp、tcp、http(get\post)、dns、ssl证书过期时间]

    Blackbox_exporter 主动监测主机与服务状态 Prometheus 官方提供的 exporter 之一,可以提供 http.dns.tcp.icmp 的监控数据采集 官方github: ...

  5. [转帖]linux磁盘IO读写性能优化

    在LINUX系统中,如果有大量读请求,默认的请求队列或许应付不过来,我们可以 动态调整请求队列数来提高效率,默认的请求队列数存放在/sys/block/xvda/queue/nr_requests 文 ...

  6. 【转帖】基于paramiko的二次封装

    https://www.jianshu.com/p/944674f44b24 paramiko 是 Python 中的一个用来连接远程主机的第三方工具,通过使用 paramiko 可以用来代替以 ss ...

  7. [转帖]从DDR到DDR4,内存核心频率基本上就没太大的进步!

    https://zhuanlan.zhihu.com/p/84194049 从2001年DDR内存面世以来发展到2019年的今天,已经走过了DDR.DDR2.DDR3.DDR4四个大的规格时代了(DD ...

  8. [转帖]一次操作系统报错OutOfMemory Error的处理记录

    在启动公司内嵌的tomcat容器时出现报错, 如下: # There is insufficient memory for the Java Runtime Environment to contin ...

  9. 一键升级Openssh的方法

    快速升级OpenSSH8.6的办法 首先: 最终的方法 创建一个文件夹,并且设置好一个启动脚本. 内容主要如下: ├── rpm │   ├── openssh-8.6p1-1.el8.x86_64. ...

  10. React中函数组件与类组件的两种使用

    React 创建组件的两种方式 函数组件:使用js函数创建的组件 约定1:函数名称必须以大写字母开头 约定2:函数组件必须要有返回值. 如果返回值为null.表示不渲染任何内容. return nul ...