翻譯 | Thingking in Redux(若是你只瞭解MVC)

做者:珂珂(滬江前端開發工程師)
本文原創,轉載請註明做者及出處。
原文地址:https://hackernoon.com/thinking-in-redux-when-all-youve-known-is-mvc-c78a74d35133#.u2jqlinjnjavascript

當咱們在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上幾篇博客的距離。java

你固然很高興了。你開始理解state和props的區別了,你知道了componentDidMount是幹啥的了,你甚至懂得了怎樣合理地建立一個可複用組件。可是突然間,你發現本身到了egghead.io網站上,這裏的一些傢伙正討論着stores、reducer compositions、action,還有將state映射到props。react

圖片描述

你同時也意識到,以前你能夠這麼作:編程

$(「.my-button」).click();

讓一個按鈕乾點什麼;如今?3個小時可能你的一個按鈕啥也幹不了。redux

做一些類比

若是你是從MVC(或者MVVC)的世界過來的,你習慣了使用models,views和controllers。可是在Redux中,咱們用actions、reducers、stores和components來解決問題。從MVC「遷移」到Redux是比較困難,但這裏是個人作法:react-native

Actions = Controller

把你的Actions想象成controller。不管什麼時候你想讓你的App產生一些活動的時候(好比:載入數據、將isLoading標誌從true變爲false等等),那麼你須要分發一個action。就像在MVC中,你須要調用一個controller。api

Reducer = Model

某種程度上吧。你的reducers將會掌管應用程序的當前狀態(好比: 用戶信息、api載入的數據、須要展現的數據)。當一個action被調用時,reducer來決定須要作些什麼。在MVC中你可能有一個帶setName()方法的model,在Redux中,你將會有一個reducer,它負責處理一個action,並將name設置到state中去。架構

特別感謝 Prabin Varma*。將store講的更生動形象。mvc

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 <<

相關文章
相關標籤/搜索