zoukankan      html  css  js  c++  java
  • 翻译 | Thingking in Redux(如果你只了解MVC)

    作者:珂珂(沪江前端开发工程师)
    本文原创,转载请注明作者及出处。
    原文地址:https://hackernoon.com/thinking-in-redux-when-all-youve-known-is-mvc-c78a74d35133#.u2jqlinjn

    当我们在Spoil打算推出我们自己的移动端应用时,头一个需要作出的决定就是:我们应该使用哪种编程语言?经过一番讨论,我们最终做出的决定是:React-Native。学习一门新的“语言”或者框架并不是个大问题,但是老兄我得告诉你,React-Native和Redux确确实实是块难啃的骨头。这篇文章没有介绍React-Native是如何工作的(因为那确实不是最难的部分)。下面几段文字的目的在于帮助任何人完成从“Thingking in MVC”到“Thinking in Redux”的转换。希望能对你有所帮助。

    React-Natvie 和 Redux?

    一旦你开始学习React-Natvie(或React),在有人向你提及Redux之前,你大概只落后了3个stack overflow的问题,或者medium.com上几篇博客的距离。

    你当然很高兴了。你开始理解state和props的区别了,你知道了componentDidMount是干啥的了,你甚至懂得了怎样合理地创建一个可复用组件。但是忽然间,你发现自己到了egghead.io网站上,这里的一些家伙正讨论着stores、reducer compositions、action,还有将state映射到props。

    图片描述

    你同时也意识到,之前你可以这么做:

    $(“.my-button”).click();
    

    让一个按钮干点什么;现在?3个小时可能你的一个按钮啥也干不了。

    作一些类比

    如果你是从MVC(或者MVVC)的世界过来的,你习惯了使用models,views和controllers。但是在Redux中,我们用actions、reducers、stores和components来解决问题。从MVC“迁移”到Redux是比较困难,但这里是我的做法:

    Actions = Controller
    

    把你的Actions想象成controller。无论何时你想让你的App产生一些活动的时候(比如:载入数据、将isLoading标志从true变为false等等),那么你需要分发一个action。就像在MVC中,你需要调用一个controller。

    Reducer = Model
    

    某种程度上吧。你的reducers将会掌管应用程序的当前状态(比如: 用户信息、api载入的数据、需要展示的数据)。当一个action被调用时,reducer来决定需要做些什么。在MVC中你可能有一个带setName()方法的model,在Redux中,你将会有一个reducer,它负责处理一个action,并将name设置到state中去。

    特别感谢 Prabin Varma*。将store讲的更生动形象。

    Stores = ???
    

    store在Redux中很特别,在MVC中难以找和它等价的东西。但是不用担心。store是深藏在幕后被小心保管的东西,就像是一个容器,存储了所有为state服务的reducer集合。它有一个方法来获得当前的状态,并且暴露出方法来订阅state的变动(使用“connect()”方法)。这就是Redux允许你调用action,并能将它们像props一样传入组件的秘密了。

    Components = Views
    

    组件是有些类似于你的智能视图。它们负责展示你从state中拿到的信息。我建议将你的组件分为两部分:一部分只是作为展示部分(木偶组件),另一部分负责处理所有的action和state变更(智能组件)。

    从MVC思想转换至Redux思想

    MVC和Redux之间一个主要的不同点就是:MVC中的数据能够双向流动,但在Redux中,数据被限制为只能单向流动。

    clipboard.png

    经典MVC。那时的人生还没有如此艰难。

    如你所见(以及从经验中了解到的)在上面的图表中,数据能够双向流动。你在view层按下了一个button,它会向你的controller发送一个信息,导致model的更新。model改变了一些值,并将值返还给controller,然后controller刷新了view。灰常简单!

    clipboard.png

    Redux数据流。人生变得糟透了。

    在Redux中事情有些不同。假如你有一个组件,然后你想在按钮被按下的时候做些事情。那么你该从何开始呢?我是这么做的:

    1. 定义你的Action
    2. 定义你的Reducer
    3. 在你的Component中将Actions像props一样定义
    4. 把它们放到View上

    下面是个解释以上概念的简单代码示例。在这个例子中,我将会展示如何编辑一个text input,然后当有用户按下按键时它将会调用action来保存内容。

    首先,从Action文件开始

    export const MODIFY_NAME = "MODIFY_NAME";
    export const SAVE_NAME = "SAVE_NAME";
    
    /**
    * 这是当用户按下按键的时候,我们从组件上所调用的action。
    用户每输入一个字符,都会带着input中新的value值去调用这个action。
    注意函数中的type和payload字段,我们将在reducer中用到它们,去用新的value值“修改”我们的model。
    **/export function modifyName(name){    return {
            type: MODIFY_NAME,
            payload:{
                name
            }
        }
    }
    
    /**
    这是当用户按下保存姓名按钮的时候,我们从组件上所调用的action。
    注意我们是如何将value传入的。这么做是因为reducer已经持有了该value值。
    另外,这里也没有payload。这么做的原因是因为reducer并不需要。在reducer那一步中,不需要额外的信息。
    同时,一般这么做将调用一个api终端以及诸如此类的东西,但是为了简洁,我没有将其包含进来。
    **/export function saveName(){ return{
       type: SAVE_NAME
     }
    }
    
    现在看我们的Reducer。总的来说,reducer需要处理传入的actions。
    
    // 导入我们早先定义好的actions文件
    import * as constants from '../actions.js';
    
    /** 
    初始状态被用来定义你的reducer。
    通常你将会把它设置为默认值和空字符串。需要这么做的理由是,当要使用这些值的时候,你至少保证它们有一个默认值。把它当做一个默认构造器吧。
    **/const initialState = {
         name:'',
         isSaved: false
    }
    
    /**
    这个reducer是负责“监听”输出的action。我们早些定义的saveName和modifyName函数,将会在这里被调用。action参数则是上面函数中定义的将要被return出来的值(type和payload)。
    **/function reducer(state=initialState,action){  switch (action.type){/**
    在Redux中state是不可变的。你必须时刻返回一个新的,所以这里使用ES6的展开运算符将传入的state中的值拷贝过来。
    **/
        case constants.MODIFY_NAME:       return {
             ...state, 
             name:action.payload.name
          }     case constants.SAVE_NAME:       return {
               ...state, 
               isSaved:!state.isSaved
           }
       }
    }
    export default name;
    

    注意到constants.MODIFY_NAME 和 constants.SAVE_NAME 是如何与我们在action中将要返回出来的对象的type字段对应上的。正是以这种方式,reducer才能得知action的身份。

    现在来定义我们的“智能”组件。这意味着这个组件将会定义所有对action的调用。

    /** App首页 **/
    ‘use strict’;
    
    import React, { Component } from ‘react’;
    import { connect } from ‘react-redux’;
    import Name from ‘./presentational/Name’;
    import * as actions from ‘./actions/name’;
    
    /**
    实际的值(name和isSaved)和调用action所需的function都以props的形式传入。**/
    class NameContainer extends Component {
     render() {
       return (
           <Name 
          name = {this.props.name} 
          isSaved = {this.props.isSaved}
          modifyName = {this.props.modifyName} 
          saveName = {this.props.saveName}
         />
       );
     }
    }
    
    /**
    这么做是为了得到reducer中保存的值,并且把它们返回给component。这样我们才能通过this.props来调用它们
    **/
    
    const mapStateToProps = (state,ownProps) =>{
    /**
    使用Redux的stores,它允许我们仅仅通过state.name来获得reducer中的值。注意name是如何通过reducer导出的。
    **/
    const { name, isSaved } = state.name; 
    return {name,isSaved };
    }
    
    /**
    在mapStateToProps函数中,我们将state中的变量映射成property来传入我们的展示组件。在mapDispatchToProps函数中,我们将action处理函数映射到我们的容器,这样我们就能将它们传入到展示组件中去了。
    **/
    
    const mapDispatchToProps = (dispatch) => { return {
     modifyName:(name)=>{
      dispatch(actions.modifyName(name))
     },
     saveName:()=>{
     dispatch(actions.saveName())
     },
    }
    
    /**
    如你所见,我们能够将函数和变量以props的方式传入我们的容器全依赖于此。是react-redux中的connect函数神奇的实现了这些功能。
    **/
    export default connect(mapStateToProps,mapDispatchToProps)(NameContainer);
    
    

    现在到了最简单的部分,创建一个与用户交互的展示组件(MVC里的V)。

    /**
     *
    木偶组件将会使用传入的props,这些是用户的行为在智能组件上产生的数据
     */‘use strict’;
    import React, { Component } from ‘react’;
    import {Text,TextInput, TouchableOpacity} from ‘react-native’;
    import { Actions, ActionConst } from ‘react-native-router-flux’;
    
    class Name extends Component
    render() { return ( <View>
    
     <TextInput
     multiline={false}
     //from the component above
     value={this.props.name}
     placeholder=”Full Name”
     //from the component above
     onChangeText={(name)=>this.props.modifyName(name)}
     />  
    
    <TouchableOpacity onPress= {()=>{this.props.saveName()}>
         <Text>Save</Text>
      </TouchableOpacity>
     </View>
     );
     }
    }
    export default Name;
    

    就是这样了!虽然你仍然需要做一些基础的的模版设置填充,但是我希望这解释清楚了如何以redux的方式进行思考。

    有些问题曾经让我掉到坑里一段时间(比如:信息传到了哪?怎么传的),因此我希望节省你们的时间,减轻你们的头疼。

    如果你希望看到我们团队使用React-Native 和 Redux搭建出来的app是什么样的,那么在这儿查看我们的app吧(https://spoil.co/app)。

    iKcamp原创新书《移动Web前端高效开发实战》已在亚马逊、京东、当当开售。

    >> 沪江Web前端上海团队招聘【Web前端架构师】,有意者简历至:zhouyao@hujiang.com <<

  • 相关阅读:
    svn command line tag
    MDbg.exe(.NET Framework 命令行调试程序)
    Microsoft Web Deployment Tool
    sql server CI
    VS 2010 One Click Deployment Issue “Application Validation did not succeed. Unable to continue”
    mshtml
    大厂程序员站错队被架空,只拿着五折工资!苟活和离职,如何选择?
    揭秘!Windows 为什么会蓝屏?微软程序员竟说是这个原因...
    喂!千万别忘了这个C语言知识!(~0 == -1 问题)
    Linux 比 Windows 更好,谁反对?我有13个赞成理由
  • 原文地址:https://www.cnblogs.com/ikcamp/p/7553126.html
Copyright © 2011-2022 走看看