翻译 | 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中,数据被限制为只能单向流动。
经典MVC。那时的人生还没有如此艰难。
如你所见(以及从经验中了解到的)在上面的图表中,数据能够双向流动。你在view层按下了一个button,它会向你的controller发送一个信息,导致model的更新。model改变了一些值,并将值返还给controller,然后controller刷新了view。灰常简单!
Redux数据流。人生变得糟透了。
在Redux中事情有些不同。假如你有一个组件,然后你想在按钮被按下的时候做些事情。那么你该从何开始呢?我是这么做的:
- 定义你的Action
- 定义你的Reducer
- 在你的Component中将Actions像props一样定义
- 把它们放到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前端高效开发实战》已在亚马逊、京东、当当开售。
翻译 | Thingking in Redux(如果你只了解MVC)的更多相关文章
- 翻译:使用 Redux 和 ngrx 创建更佳的 Angular 2
		翻译:使用 Redux 和 ngrx 创建更佳的 Angular 2 原文地址:http://onehungrymind.com/build-better-angular-2-application- ... 
- 如果你只会JQuery的插件式开发, 那么你可以进来看看?
		对于JQuery的学习,已经有3年多的时间了,直到去年与我的组长一起做项目,看到他写的JS,确实特别漂亮,有时甚至还看不太懂, 我才发现其实我不太会JQuery.所以我有时间就会去看看他写的JS代码, ... 
- 这个时候 快下班了 我来翻译一段: Pro ASP.NET MVC 3 Framework
		Binding to a Derived Type绑定派生类型Although we have focused on interfaces (since that is most relevant i ... 
- trove命令翻译(上)(只做翻译,未实验效果)
		The trove client is the command-line interface (CLI) for the Database service API and its extensions ... 
- ASP.NET MVC 的 WebGrid 的 6 个重要技巧 【已翻译100%】
		ASP.NET MVC 中 WebGrid 的 6 个重要技巧 https://www.oschina.net/translate/webgrid-in-asp-net-mvc-important-t ... 
- MVC面试问题与答案
		读这篇文章不意味着你一定要去并且能搞定MVC面试.这篇文章的目的是在面试之前让你快速复习MVC知识.这篇文章也不是MVC培训课程. 如果你想学习MVC,从这儿开始 Learn MVC ( Model ... 
- ASP.NET与ASP.NET  MVC 中Cache的总结
		Cache有多种翻译,可以是高速缓冲存储器,也可以是法国的服装品牌,本文只是简单的谈谈就是ASP.NET 中Cache,做过Web应用程序的都知道,如果网站访问量比较大,系统应用程序可以将那些频繁访问 ... 
- Spring Security 5.0.x 参考手册 【翻译自官方GIT-2018.06.12】
		源码请移步至:https://github.com/aquariuspj/spring-security/tree/translator/docs/manual/src/docs/asciidoc 版 ... 
- MVC框架
		MVC (Modal View Controler)本来是存在于Desktop程序中的,M是指数据模型,V是指用户界面,C则是控制器.使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用 ... 
随机推荐
- java web轻量级开发面试教程内容精粹:哪些简历得不到面试机会
			看到一本较好的实践性比较强的书,java web轻量级开发面试教程,里面的一些内容很有实践意义. 问题点 很难获得面试机会的原因 学历不符,比如要求是本科以上,但学历是大专 学历是硬指标,所以达不到学 ... 
- 环境配置-云服务器jdk与tomcat配置
			我所实践的主机是麻花疼云的主机,第一次试用30天,装了个centos6.5,其实已经用商用专用镜像配置好环境了,包括jdk.tomcat等常用的内容,但是我实在是找不到他们安装在哪个目录下了,我就自己 ... 
- python基础教程(九)
			python异常 python用异常对象(exception object)来表示异常情况.遇到错误后,会引发异常.如果异常对象并未被处理或捕捉,程序就会用所谓的 回溯(Traceback, 一种错误 ... 
- Hue集成Hadoop和Hive
			一.环境准备 1.下载Hue:https://dl.dropboxusercontent.com/u/730827/hue/releases/3.12.0/hue-3.12.0.tgz 2.安装依赖 ... 
- MySQL(四)--蠕虫复制、查询
			1 蠕虫复制 蠕虫复制:从已有的数据中去获取数据,然后将数据又进行新增操作,数据成倍增加. 表创建高级操作:从已有创建新表(复制表结构) create table 表名 like 数据库.表名; 蠕虫 ... 
- 【2017集美大学1412软工实践_助教博客】团队作业7——Alpha冲刺之事后诸葛亮
			题目 团队作业7: http://www.cnblogs.com/happyzm/p/6827853.html 团队成绩 评分项目 变更管理 设计/实现 测试/发布 团队的角色,管理,合作 总结 全组 ... 
- 微信小程序icon,text,progress标签的测试
			一:testIconAndTextAndProgress.wxml的代码如下.testIconAndTextAndProgress.js自动生成示例代码 //testIconAndTextAndPro ... 
- 201521123054《Java程序设计》第8周学习总结
			1. 本周学习总结 2. 书面作业 List中指定元素的删除(题目4-1) 1.1 实验总结 每次删除时下标需要-1:原理如图 统计文字中的单词数量并按出现次数排序(题目5-3) 2.1 伪代码(简单 ... 
- 201521123112《Java程序设计》第14周学习总结
			1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 关系型数据库系统:使用表来存储数据,使用行来区分不同记录. 主键可以唯一确定一条记录. 常见的数据库管理系统有: ... 
- 201521123038 《Java程序设计》 第十周学习总结
			201521123038 <Java程序设计> 第十周学习总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题 ... 
