翻譯 | Thingking in Redux(如果你只瞭解MVC)

来源:http://www.cnblogs.com/ikcamp/archive/2017/09/19/7553126.html
-Advertisement-
Play Games

當我們在Spoil打算推出我們自己的移動端應用時,頭一個需要作出的決定就是:我們應該使用哪種編程語言?經過一番討論,我們最終做出的決定是:React-Native。學習一門新的“語言”或者框架並不是個大問題,但是老兄我得告訴你,React-Native和Redux確確實實是塊難啃的骨頭。這篇文章沒有... ...


作者:珂珂(滬江前端開發工程師)
本文原創,轉載請註明作者及出處。
原文地址: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前端架構師】,有意者簡歷至:[email protected] <<


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 接著這篇文章[js高手之路]Node.js+jade抓取博客所有文章生成靜態html文件繼續,在這篇文章中實現了採集與靜態文件的生成,在實際的採集項目中, 應該是先入庫再選擇性的生成靜態文件。 那麼我選擇的資料庫是mongodb,為什麼用這個資料庫,因為這個資料庫是基於集合,數據的操作基本是json ...
  • 這兩種寫法。這兩種寫法到底有什麼不同呢?用哪種來寫更加規範呢? 將href="#"是指聯接到當前頁面,其實是無意義的,頁面也不會刷新。這是一個錨鏈接。 在製作網頁時html語言里的參數,用於指定鏈接的url ####就是本頁鏈接,href="地址"就是鏈接到地址 鏈接本頁面 預設本頁,不彈出新視窗, ...
  • <!DOCTYPE html><html><head lang="zh-cn"><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><m ...
  • function getobj(objs, key, value) { for (var i in objs) { var obj = $(objs[i]); if (obj.attr(key) == value) { return obj[0]; } } return null; }; ...
  • 首先瞭解下CSS的渲染邏輯,它是從標記的最後一位開始搜索的,例如:.myclass li a,首選它會遍歷所有的<a>,然後看哪些<a>之前有<li>,然後再看哪些<li>之前有.myclass。 所以:1、層級太多會增加CSS渲染的工作量。 如下: 除此之外,還有哪些可以優化的呢? 2、圖中樣式的 ...
  • 接著上篇vue-cli腳手架構建項目結構建好項目之後,就開始寫個“hello world!”吧~~~ vue玩的都是組件,所以開發的也是組件。 1.新建helloworld.vue。(刪除Hello.vue)代碼如下: 一個簡單的組件就完成了。 2.我們打開入口組件App.vue並把裡面的代碼替換成 ...
  • 使用 react已經有不短的時間了,最近看到關於 react高階組件的一篇文章,看了之後頓時眼前一亮,對於我這種還在新手村晃蕩、一切朝著打怪升級看齊的小嘍啰來說,像這種難度不是太高同時門檻也不是那麼低的東西如今可不多見了啊,是個不可多得的 zhuangbility的利器,自然不可輕易錯過,遂深入瞭解 ...
  • 簡介 在SF上看到這樣一個提問: 如題,因為不得已的原因,需要寫若幹個全局函數。但又不想這樣: 題主問有什麼好的寫法? 解答: 如果你用 jQuery,你可以這樣寫 如果你不用 jQuery,可以直接實現類似的 extend: 在JavaScript中,命名空間可以幫助我們防止與全局命名空間下的其他 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...