MyException - 我的异常网
当前位置:我的异常网» VC/MFC » 通译 | Thingking in Redux(如果你只了解MVC)

通译 | Thingking in Redux(如果你只了解MVC)

www.MyException.Cn  网友分享于:2013-10-08  浏览:0次
翻译 | 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 <<

文章评论

总结2014中国互联网十大段子
总结2014中国互联网十大段子
那些争议最大的编程观点
那些争议最大的编程观点
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
代码女神横空出世
代码女神横空出世
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
程序员应该关注的一些事儿
程序员应该关注的一些事儿
程序员和编码员之间的区别
程序员和编码员之间的区别
中美印日四国程序员比较
中美印日四国程序员比较
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
我的丈夫是个程序员
我的丈夫是个程序员
一个程序员的时间管理
一个程序员的时间管理
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
 程序员的样子
程序员的样子
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
鲜为人知的编程真相
鲜为人知的编程真相
为什么程序员都是夜猫子
为什么程序员都是夜猫子
程序员必看的十大电影
程序员必看的十大电影
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
老程序员的下场
老程序员的下场
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
漫画:程序员的工作
漫画:程序员的工作
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
每天工作4小时的程序员
每天工作4小时的程序员
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
如何成为一名黑客
如何成为一名黑客
我是如何打败拖延症的
我是如何打败拖延症的
编程语言是女人
编程语言是女人
10个调试和排错的小建议
10个调试和排错的小建议
旅行,写作,编程
旅行,写作,编程
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
程序员的鄙视链
程序员的鄙视链
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有