前言

  这篇博文接 【前端】react and redux教程学习实践,浅显易懂的实践学习方法。 ,上一篇简略的做了一个redux的初级demo,今天深入的学习了一些新的、有用的,可以在生产项目中使用的前端架构,我将尽量以最简单的语言描述,如果有童鞋看不懂,也可以私下问我。

复习

  前一节我们已经知道,一个redux应用,主要有几个概念,它们的共同作用都是管理一个全局state,使react组件的state集中处理,想一下你在写react组件的时候,组件的state总是或多或少与父级组件有关联,一般情况下我们都是用props处理的 —— 即在父级组件传入state,子组件使用props管理状态state。然而经过redux的处理,state被提升到最高等级的组件,它被具体化成一个对象,变得更加容易管理,可以直接在后端读取state改变前端交互。复习一下两个概念:

  1.store 

  Store 就是保存状态数据对象的地方,你可以把它看成一个容器。整个应用只能有一个 Store。这个对象通过createStore创建,它的第一个参数传入一个函数,这个函数就是唯一能处理改变state的地方。

import { createStore } from 'redux';
const store = createStore(fn);

  2、action

   action是一个通知,例如我有一个组件,里面有一个click事件改变了当前的状态,那么就要通过这个click事件的回调,向store发出要更改state的action,store传入的那个函数接到通知以后,改变state,最后更新视图。发出action的方法是,一般action是一个对象,官方规定它必须有一个type属性,用来规定现在这个action是谁发出的。

store.dispatch(action);

好,就复习这两个概念,这几个流程可以概括为:

首先组件A通过回调发出action,然后reducers接受到这个action,处理完之后返回state对象更改,最后触发视图更新。一个redux交互的流程大概就是这样。

要看得懂下面的东西,你需要懂上一节写过的代码。

先实践

还是实践那个案例,一个number的加减:

目标是点击对应的按钮,数字加减,输入时值也随state变化。github地址:https://github.com/294678380/redux_demo   (demo2)

先来看看上次我们是怎么发送action的:

我们通过直接在组件上挂props实现的向store发出action

const render = ()=> ReactDOM.render(
<div>
<Number
value={store.getState()}
add={(action)=>store.dispatch(action)} //传入一个函数,传入发送过来的action,由reducers处理之后返回state,
less={(action)=>store.dispatch(action)}
/>
</div>,
content
);
class Number extends Component{
add(){
this.props.add({type:"NUMBER_ADD"}) //调用app.js中传过来的箭头函数,传入了type为NUMBER_ADD的action
}
  //.....省略
}

通过传过来的add箭头函数,发出了type是NUMBER_ADD的action,然后reducers接受到了这个action,更改了state引发视图更新。

现在我们要改造这份代码

为什么这样的方式有问题?

现在这样的代码能看到的至少有三点问题:

  1.发出的那个action是组件内确定的,结果就是,我需要在组件内写一个type,比如那个“NUMBER_ADD”,在reducers处理函数中还要再判断type等于“NUMBER_ADD”,如果需要更改,增加了应用耦合。

  2.监听是全局的,也就是说每次更新都是全局的。这影响效率。

  3.代码也不够优雅,要填写很多重复的props。

让我们来看代码:

  app.js

import React from "react";
import ReactDOM from "react-dom";
import {InitReducers} from "./reducers";
import {createStore} from "redux";
//这个是react-redux提供的管理state的组件,把它放在最高层就可以实现统一管理state。
import {Provider} from "react-redux";
import Number from "./number";
//createStore生成store,InitReducers在收到action时调用的函数
const store = createStore(InitReducers);
const content = document.querySelector(".content");
//直接render
ReactDOM.render(
<Provider store={store}>
<Number />
</Provider>,
content
);

入口做了更改吧监听函数store.subscribe()移除,使用Provider包裹的方式发布state,注意 createStore(InitReducers); 传入的这个函数是从reducers.js导出的InitReducers函数。

现在我们看reduces.js

import * as Actions from "./actions";
const ReducersActions = {
[Actions.NUMBER_ADD]:(state,action)=>{
state.number++;
return state;
},
[Actions.NUMBER_LESS]:(state,action)=>{
state.number--;
return state;
},
[Actions.NUMBER_VALUE]:(state,action)=>{
state.number = action.value;
return state;
} }
function InitReducers(state={},action){
state.number == undefined?state.number=:state.number;
if(action.type === "@@redux/INIT"){
return state;
}
state = ReducersActions[action.type](state,action);
return Object.assign({},state);
}
export {InitReducers}

引入了actions.js文件,这里就是要解决前面说的,action需要一个统一入口的问题。这个actions.js定义了所有action.type,函数 InitReducers被触发时,ReducersActions[action.type](state,action);会被调用,这个action.type是组件发过来的action。

现在我们看actions.js

export const NUMBER_ADD = "NUMBER_ADD";
export const NUMBER_LESS = "NUMBER_LESS";
export const NUMBER_VALUE = "NUMBER_VALUE";
export function setNumber(type,value=false){
switch(type){
case "add":
return {type:NUMBER_ADD}
case "less":
return {type:NUMBER_LESS}
case "value":
return {type:NUMBER_VALUE,value:value}
}
}

我在这里定义了三个action,

NUMBER_ADD     增加

NUMBER_LESS  减少
NUMBER_VALUE  输入

一个函数
setNumber  这个函数是给number组件调用的。

然后number.js

import React,{Component} from "react";
import {connect} from "react-redux";
//bindActionCreators
import {bindActionCreators} from "redux";
//引入actions
import * as Actions from "./actions"; const propTypes = {
actions:React.PropTypes.object.isRequired,
number:React.PropTypes.number.isRequired
}
class Number extends Component{
setNumber(event){
let {target} = event;
let type = target.getAttribute("data-type");
if(type != "value"){
this.props.actions.setNumber(type);
}else{
this.props.actions.setNumber(type,target.value);
}
}
render(){
let props = this.props,
setNumber = this.setNumber.bind(this);
return <div>
<input type="text" onChange={setNumber} data-type="value" value={props.number}/>
<button data-type="add" onClick={setNumber}>+</button>
<button data-type="less" onClick={setNumber}>-</button>
</div>
}
}
Number.propTypes = propTypes;
//定义组件内需要使用的state某个值
function mapStateToProps(state){
return {
number:state.number
}
}
//定义组件内需要调用回调改变state的props参数
function mapDispatchToProps(dispatch){
return {
//这就是合并actions.js中定义的函数,在组件内this.props.actions.xxx调用bindActionCreators生成的dispatch函数
actions:bindActionCreators(Actions,dispatch)
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Number);

先把视线转到button的click:

 调用了setNumber方法

 setNumber调用了  this.props.actions.setNumber(type,target.value); 传入type,如果是输入则传入value,这里调用的就是actions.js中export出来的setNumber函数,那么它是怎么传进来的呢?

  

//定义组件内需要使用的state某个值
function mapStateToProps(state){
return {
number:state.number
}
}
//定义组件内需要调用回调改变state的props参数
function mapDispatchToProps(dispatch){
return {
//这就是合并actions.js中定义的函数,在组件内this.props.actions.xxx调用bindActionCreators生成的dispatch函数
actions:bindActionCreators(Actions,dispatch)
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Number);

就是这一段,

第一个函数,mapStateToProps 这个是定义组件内需要获取的,state的值,就是你全局的状态会被传入这个函数,你需要指定哪一个属性你需要放到组件的props里面。

第二个函数,mapDispatchToProps 这个是定义组件内需要获取的,能调用到 dispatch函数的回调。它也是放到props里面,和第一个state的区别是,这里放进去的是函数,是能够调用actions.js里定义的函数。

  这里的

return {
//这就是合并actions.js中定义的函数,在组件内this.props.actions.xxx调用bindActionCreators生成的dispatch函数
actions:bindActionCreators(Actions,dispatch)
}

这个actions就是那句 this.props.actions对象,它的值是 bindActionCreators(Actions,dispatch)。这个函数把actions.js中所有导出的函数转换成能够触发dispatch的函数,在组件里调用this.props.actions.setNumber时,实际上在setNumber处理完action之后,又会自动调用store.dispatch这个方法发出action。

然后总结要点

  要点一:Provider组件包裹整个应用,这样就能通过这一个超级父组件管理所有子组件的state。

  要点二:bindActionCreators函数,绑定action,创建dispatch。返回一个actions对象,然后传入组件中,最后组件调用actions里面定义的方法,方法处理action调用store.dispatch,这是整个架构的关键点

看一点别人的项目实践

  最近在看一个项目:superset。 这是一个大数据可视化集成软件,它的前端架构是基于react+redux的,里面的整体架构,可以看到如图:

app.jsx:

看看它的做法,它将actions对象分发到需要全局state的所有组件中,然后在这些组件中就不需要定义任何与redux相关的代码了:

最后总结

打字到这里,手有点酸,先不总结啦。

demo代码github:https://github.com/294678380/redux_demo

=============================

转载需在明显处注明作者与出处链接。

【前端,干货】react and redux教程学习实践(二)。的更多相关文章

  1. 【前端】react and redux教程学习实践,浅显易懂的实践学习方法。

    前言 前几天,我在博文[前端]一步一步使用webpack+react+scss脚手架重构项目 中搭建了一个react开发环境.然而在实际的开发过程中,或者是在对源码的理解中,感受到react中用的最多 ...

  2. immutable.js 在React、Redux中的实践以及常用API简介

    immutable.js 在React.Redux中的实践以及常用API简介 学习下 这个immutable Data 是什么鬼,有什么优点,好处等等 mark :  https://yq.aliyu ...

  3. 前端框架React Js入门教程【精】

    现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和组件化的开发深深的吸引了我,下面来跟我一起领 ...

  4. 【REACT NATIVE 系列教程之十二】REACT NATIVE(JS/ES)与IOS(OBJECT-C)交互通信

    http://blog.csdn.net/xiaominghimi/article/details/51586492 一用到跨平台的引擎必然要有引擎与各平台原生进行交互通信的需要.那么Himi先讲解R ...

  5. 2015年最热门前端框架React 入门实例教程

    现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Face ...

  6. 不会几个框架,都不好意思说搞过前端: React 入门实例教程

    现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Face ...

  7. React Native小白入门学习路径——二

    万万没想到,RN组仅剩的一个学长也走了,刚进实验室没几天就被告知这样的事情,一下子还真的有点接受不了,现在RN组就成了为一个没有前辈带的组了,以后学习就更得靠自己了吧.唉,看来得再努力一点了. 这一周 ...

  8. 模板学习实践二 pointer

    c++ template学习记录 使用模板将实际类型的指针进行封装 当变量退出作用域 自动delete // 1111.cpp : 定义控制台应用程序的入口点. // #include "s ...

  9. SQL 初级教程学习(二)

    1.SQL 语句从 "Websites" 表中选取头两条记录: SELECT * FROM Websites LIMIT 2; SELECT TOP 50 PERCENT * FR ...

随机推荐

  1. Android 设计模式实战之关于封装计费代码库的策略模式详谈

    写在之前 这周生活上出现了很多的不如意,从周一开始就觉得哪里出现了问题,然后就是各种烦躁的情绪,后来事情还真是如预感的那样发生了,很是心痛,但也无可奈何,希望大家都好好珍惜自己身边的人:友人,亲人,家 ...

  2. JS对象创建常用方式及原理分析

    ====此文章是稍早前写的,本次属于文章迁移@2017.06.27==== 前言 俗话说"在js语言中,一切都对象",而且创建对象的方式也有很多种,所以今天我们做一下梳理 最简单的 ...

  3. javascript中apply,call,bind区别,bind兼容等问题总结

    1 三者的相似之处: (1).都是用来改变函数的this对象的指向的 (2).都是用第一个参数来做this对象的指向 (3).都可以传参数进去 那么,具体到它们有什么区别呢?请看下面的例子: 两个对象 ...

  4. object覆盖的div解决办法

    最近做个web项目,因为里面有个<object>的插件,弹出<div>对话框会被其遮盖,我做了个<iframe>标签,在弹框时,把<object>覆盖掉 ...

  5. 如何使用Node.js编写命令工具——以vue-cli为例

    vue-cli全局安装之后,提供了vue命令和vue init.vue list.vue build三个子命令,通过命令可以搭建基于vue.js的脚手架项目.本文简单介绍一下这些命令是如何实现的. v ...

  6. NoSQL数据库:Redis适用场景及产品定位

    传统MySQL+ Memcached架构遇到的问题 实际MySQL是适合进行海量数据存储的,通过Memcached将热点数据加载到cache,加速访问,很多公司都曾经使用过这样的架构,但随着业务数据量 ...

  7. jQuery从入门到忘记

    jQuery 是一套Javascript脚本库,注意 jQuery 是脚本库,而不是脚本框架."库"不等于"框架".jQuery 并不能帮助我们解决脚本的引用管 ...

  8. Linux 安装依赖库

    ###安装依赖库###yum -y install rsync net-snmp syslog net-snmp-devel wget patch screen gcc gcc-c++ autocon ...

  9. gulp inline

    在html中所有需要内敛的文件 script link 后面都要写上inline 这样才能够,内敛到文件中.

  10. [图形学] Chp8.7.2 梁友栋-Barsky线段裁剪算法

    这节简单介绍了梁友栋-Barsky裁剪算法的原理,只有结论并没有过程,看过http://blog.csdn.net/daisy__ben/article/details/51941608这篇文章后,大 ...