zoukankan      html  css  js  c++  java
  • 【前端,干货】react and redux教程学习实践(二)。

    前言

      这篇博文接 【前端】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=0: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

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

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

  • 相关阅读:
    Effective Java 第三版——72. 赞成使用标准异常
    Effective Java 第三版——71. 避免不必要地使用检查异常
    Effective Java 第三版——70. 对可恢复条件使用检查异常,对编程错误使用运行时异常
    Effective Java 第三版——69. 仅在发生异常的条件下使用异常
    Effective Java 第三版——68. 遵守普遍接受的命名约定
    Effective Java 第三版——67. 明智谨慎地进行优化
    Effective Java 第三版——66. 明智谨慎地使用本地方法
    Effective Java 第三版——65. 接口优于反射
    Effective Java 第三版——64. 通过对象的接口引用对象
    Effective Java 第三版——63. 注意字符串连接的性能
  • 原文地址:https://www.cnblogs.com/ztfjs/p/redux_demo2.html
Copyright © 2011-2022 走看看