ReactNative的組件架構(gòu)設(shè)計

本篇較長圆裕,前面是目前flux開源框架的一些分析取逾,后面是架構(gòu)設(shè)計過程蜓竹。您可以直奔主題箕母。

用RN最大的難題是設(shè)計思想的轉(zhuǎn)變,以前的設(shè)計方法論已經(jīng)不太適用了俱济。而RN僅僅提供了view的框架嘶是,構(gòu)建完整app的架構(gòu)并沒有直接提供。

考慮目前遇到的如下問題蛛碌,希望架構(gòu)給出解決方案聂喇。

  • 交互:如何解決組件間通信【父子、子父蔚携、兄弟等希太,特別是跨層or反向數(shù)據(jù)流動等】;用state還是接口操作組件酝蜒;
  • 職責(zé):組件狀態(tài)放哪誊辉,業(yè)務(wù)邏輯放哪,數(shù)據(jù)放哪秕硝,因為太靈活了芥映,怎么做都可以實現(xiàn)功能洲尊,但是怎么做才是最好的远豺,才是最正確的呢?

todo一個問題:由于react是面向狀態(tài)編程坞嘀,相當(dāng)于react的組件只關(guān)注數(shù)據(jù)的最終狀態(tài)躯护,數(shù)據(jù)是怎么產(chǎn)生的并不關(guān)心,但是某些場景下丽涩,數(shù)據(jù)如何產(chǎn)生的是會影響到組件的一些行為的【比如一個新增行要求有動畫效果棺滞,查詢出的行就不需要等】,這在RN中很難描述矢渊。继准。。矮男。移必。

RN架構(gòu)就是為解決上述問題提供的指導(dǎo)和方法論,是通盤考慮整個開發(fā)毡鉴、測試秒赤、運維的狀況憎瘸,做出的考慮最全面的抉擇,或者為抉擇提供依據(jù)潮售。

目前為react服務(wù)的架構(gòu)也有一些了锅风,如Flux遏弱,Reflux,Redux泪姨,Relay饰抒,Marty袋坑。

Flux

flux是官方提供的架構(gòu),目的是分層解耦婆誓,職責(zé)劃分清晰也颤,誰負(fù)責(zé)干啥很明確。具體描述可以參考官方文檔文留,這里不詳述竭沫。

  • action 封裝請求
  • dispatcher 注冊處理器、分發(fā)請求
  • store 是處理器蜕提,處理業(yè)務(wù)邏輯,保存數(shù)據(jù)
  • view 根據(jù)store提供的數(shù)據(jù)進行展現(xiàn);接受用戶的輸入并發(fā)出action請求拄氯。

數(shù)據(jù)流動:Action-> Dispatcher -> Store -> Component

但我覺得解耦的太細(xì)了,干一個事镣煮,要做太多太多的額外工作了鄙麦。

光注冊監(jiān)聽動作就2次,一次是store注冊到dispatcher介衔,一次是view注冊到store中炎咖。

而且寒波,注冊到dispatcher的監(jiān)聽?wèi)?yīng)該都不叫注冊俄烁,架構(gòu)完全沒有提供任何封裝,直接暴露一個統(tǒng)一的回調(diào)方法粹胯,里面自行if else路由不同的store辰企。

Reflux

結(jié)構(gòu)上與flux架構(gòu)基本一致,去掉了flux的一些冗余操作【比如沒有了dispatcher】议忽,架構(gòu)更加簡潔和緊湊十减,用到了一些約定大于配置的理念愤估。

基本上將flux的架構(gòu)冗余都簡化了玩焰,可以說是flux的去冗余提升版,但是沒有本質(zhì)的變化蔓榄。

╔═════════╗       ╔════════╗       ╔═════════════════╗
║ Actions ║──────>║ Stores ║──────>║ View Components ║
╚═════════╝       ╚════════╝       ╚═════════════════╝
     ^                                      │
     └──────────────────────────────────────┘
  • 更容易的監(jiān)聽并炮。listenables和約定以on開頭的方法甥郑。等逃魄。
  • 去掉了dispatcher澜搅。
  • action可以進行aop編程伍俘。
  • 去掉了waitfor。store可以監(jiān)聽store癌瘾。
  • component提供了一系列mixin,方便注冊\卸載到store的監(jiān)聽和與store交互等饵溅。

Redux

社區(qū)內(nèi)比較受推崇妨退,因為用起來相對比較簡單

特性:

1、分層設(shè)計碧注,職責(zé)清晰。
2、要求store reducer都是頁面單例放典,易于管理奋构。
3、action為請求dto對象弥臼,是請求類型,請求數(shù)據(jù)的載體。
4纳猪、reducer是處理請求的方法氧卧。不允許有狀態(tài),必須是純方法氏堤。必須嚴(yán)格遵守輸入輸出沙绝,中間不允許有異步調(diào)用。不允許對state直接進行修改,要想修改必須返回新對象闪檬。
5星著、store

  • 維持應(yīng)用的state;
  • 提供 getState() 方法獲取 state粗悯;
  • 提供 dispatch(action) 方法分發(fā)請求來更新 state虚循;門面模式,要求所有的請求滿足統(tǒng)一的格式【可以進行路由为黎、監(jiān)控邮丰、日志等】,統(tǒng)一的調(diào)用方式铭乾。
  • 通過 subscribe(listener) 注冊監(jiān)聽器監(jiān)聽state的變化剪廉。

6、官方文檔寫的較為詳細(xì)炕檩,從設(shè)計到開發(fā)都有斗蒋,比flux要好

痛處如下,看能否接受或者解決:

1笛质、redux的原則:state不能被修改泉沾。

  • 其實這個用react的state也會有同樣的問題,最好把state設(shè)計的沒有冗余妇押,盡量少出這種情況
  • 解決方案:參考官方跷究,因為我們不能直接修改卻要更新數(shù)組中指定的一項數(shù)據(jù),這里需要先把前面和后面都切開敲霍。如果經(jīng)常需要這類的操作俊马,可以選擇使用幫助類 React.addons.update,updeep肩杈,或者使用原生支持深度更新的庫 Immutable柴我。最后,時刻謹(jǐn)記永遠(yuǎn)不要在克隆 state 前修改它扩然。

2艘儒、單一的龐大的reducer的拆分

  • 這塊設(shè)計也不好做,會讓人疑惑
  • 官方給的demo中直接按state的內(nèi)容區(qū)分夫偶,我覺得這樣做不好界睁,如果后期有跨內(nèi)容的情況,就比較奇怪了索守。官方給的combineReducers方案晕窑,也只是減少代碼量,本質(zhì)沒有變化卵佛,state還是拆分處理,路由還是業(yè)務(wù)邏輯自己來做。
  • 解決方案:還是處理一整個state截汪,可以按照約定寫reducer類而不是方法疾牲,類里按照actionType建方法,架構(gòu)自動路由并調(diào)用衙解。
  • 以前做java架構(gòu)阳柔,路由一定是架構(gòu)來調(diào)用的,目前感覺各大flux框架都是解決問題不徹底蚓峦。

3舌剂、官方建議設(shè)計模式:頂層容器組件才對redux有依賴,組件間通過props來傳遞數(shù)據(jù)暑椰。按照這樣設(shè)計還是沒有解決組件間交互和數(shù)據(jù)傳遞的問題霍转。官方react設(shè)計建議:react的設(shè)計建議:http://camsong.github.io/redux-in-chinese/docs/basics/UsageWithReact.html
4、使用connect將state綁定到component一汽。此處有些黑盒了避消。
5、異步action用來請求服務(wù)端數(shù)據(jù),利用middleware增強createStore的dispatch后即支持召夹。

結(jié)論

開源架構(gòu)封裝的簡單的flux會產(chǎn)生較多的冗余代碼岩喷。

開源架構(gòu)封裝的復(fù)雜的redux,其和RN綁定封裝了一些東西监憎,是一個黑盒纱意,不易理解和維護。

介于上述兩者之間的開源架構(gòu)reflux鲸阔,文檔較上述2個少偷霉,不知道其可持續(xù)性如何。如果一定要用開源架構(gòu)的話隶债,我覺得他稍加封裝是一個較為推薦的選擇腾它。

不是特復(fù)雜的程序【一般spa的程序會更復(fù)雜一些,而RN并不是spa】死讹,這些概念只會增加你的開發(fā)難度瞒滴,并且對后面維護的人要求更高。

我們繼續(xù)頭腦風(fēng)暴赞警,繼續(xù)抽象總結(jié)一下flux系列框架妓忍, flux系列框架干了什么,沒干什么愧旦,針對開篇提出的問題世剖。

  • 【解決職責(zé)】flux系列框架都做到了解耦,分層笤虫,誰該干什么就干什么旁瘫,不許干別的祖凫,讓代碼讀起來更有預(yù)測性和一致性,方便維護
  • 【解決通信】繼續(xù)解耦酬凳,flux系列框架采用事件機制解決各層之間通信惠况,采用props傳遞解決各組件之間通信。

事件系統(tǒng)是關(guān)鍵

flux系列架構(gòu)解決通信問題的方法是使用事件系統(tǒng)宁仔,事件系統(tǒng)中的回調(diào)函數(shù)是業(yè)務(wù)邏輯稠屠,redux是【store action reducer】,flux是【action dispacher store】翎苫。

我們真的需要事件系統(tǒng)嗎权埠?

事件系統(tǒng)的好處:

  • 一個事件可以注冊多個回調(diào)函數(shù)
  • 各回調(diào)函數(shù)間沒有耦合。

關(guān)于1

需要注冊多個的這種情況并不多見煎谍,不信你去翻看你已經(jīng)寫好的代碼攘蔽,是不是大部分都是注冊一個。

關(guān)于2

解耦確實很徹底粱快,但是當(dāng)我需要控制執(zhí)行順序秩彤,需要等a執(zhí)行完在執(zhí)行b,怎么辦事哭?ok你可以先注冊a在注冊b啊漫雷。那a要是一個fetch或ajax操作呢?這時候只能乖乖的在a的請求結(jié)束回調(diào)函數(shù)中進行調(diào)用b了鳍咱。又變成a依賴b了降盹。當(dāng)然,你可以繼續(xù)dispatch(b)谤辜,這就沒有耦合了蓄坏。但是你要知道注冊一個事件是要有成本的,要寫action丑念,而且這種dispatch的方式涡戳,真的不太適合人類的閱讀,dispatch一下脯倚,下一步都有誰來執(zhí)行都不知道渔彰,這哪有直接調(diào)用來的爽快。

好吧說到這推正,最后的結(jié)論也出來了恍涂,不使用開源架構(gòu),借助其好的思想植榕,替換其事件系統(tǒng)為面向?qū)ο蠼Y(jié)構(gòu)再沧,自行封裝架構(gòu)。

架構(gòu)設(shè)計

再次強調(diào):目前僅考慮如何應(yīng)用于react native

先扣題尊残,針對開篇問題的解決方案如下

交互

1炒瘸、組件對外發(fā)布:組件對外只允許使用props來暴露功能淤堵,不允許使用接口及其它一切方式
2、父子組件間:組件的子組件通過父組件傳遞的接口來與父組件通信
3什燕、兄弟組件間:

  • 方案1:假設(shè)a要調(diào)用b粘勒,參考第一條的話竞端,其實就是a要改變b的props屎即,那么a只要改b的props的來源即可,b的props的來源一般就是根組件的state事富。那么根組件就要有組織和協(xié)調(diào)的能力技俐。
  • 方案2:利用事件機制,基本同flux架構(gòu)统台。略復(fù)雜雕擂,且我們并不需要事件的特性,本架構(gòu)設(shè)計不推薦贱勃。
職責(zé)

1井赌、root-存放state,組織子view組件贵扰,組織業(yè)務(wù)邏輯對象等
2仇穗、子view組件-根據(jù)this.props渲染view。
3戚绕、業(yè)務(wù)邏輯對象-提供業(yè)務(wù)邏輯方法

根據(jù)以上推導(dǎo)纹坐,我將其命名為面向?qū)ο蟮腞eactNative組件架構(gòu)設(shè)計,它與flux系列架構(gòu)的最大的不同之處在于舞丛,用業(yè)務(wù)邏輯對象來代替了【store action dispatcher】or【store reducer】的事件系統(tǒng)耘子。業(yè)務(wù)邏輯對象就是一組對象,用面向?qū)ο蟮脑O(shè)計理念設(shè)計出的n個對象球切,其負(fù)責(zé)處理整個頁面的業(yè)務(wù)邏輯谷誓。

以上為推導(dǎo)過程,干貨才開始吨凑。捍歪。。怀骤。

面向?qū)ο蟮腞eactNative組件\頁面架構(gòu)設(shè)計

一個獨立完整的組件\頁面一般由以下元素構(gòu)成:

1费封、root組件,1個蒋伦,

  • 負(fù)責(zé)初始化state
  • 負(fù)責(zé)提供對外props列表
  • 負(fù)責(zé)組合子view組件形成頁面效果
  • 負(fù)責(zé)注冊業(yè)務(wù)邏輯對象提供的業(yè)務(wù)邏輯方法
  • 負(fù)責(zé)管理業(yè)務(wù)邏輯對象

2弓摘、view子組件,0-n個痕届,

  • 根據(jù)props進行視圖的渲染

3韧献、業(yè)務(wù)邏輯對象末患,0-n個,

  • 提供業(yè)務(wù)邏輯方法
root組件

root組件由以下元素組成:

1锤窑、props-公有屬性
2璧针、state-RN體系的狀態(tài),必須使用Immutable對象
3、私有屬性
4渊啰、業(yè)務(wù)邏輯對象的引用-在componentWillMount中初始化
5探橱、私有方法-以下劃線開頭,內(nèi)部使用or傳遞給子組件使用
6绘证、公有方法【不推薦】隧膏,子組件和外部組件都可以用,但不推薦用公有方法來對外發(fā)布功能嚷那,破壞了面向狀態(tài)編程胞枕,盡可能的使用props來發(fā)布功能

子view組件

子view組件中包含:

1、props-公有屬性
2魏宽、私有屬性-強烈不建議有腐泻,除非你能理解以下幾點,建議放在父組件or業(yè)務(wù)邏輯對象中

  • 絕對不允許和父組件的屬性or狀態(tài)有冗余队询。無論是顯性冗余還是計算結(jié)果冗余派桩,除非你能確定結(jié)算是性能的瓶頸。
  • 此屬性只有自己會用娘摔,父組件和兄弟組件不會使用窄坦,如果你不確定這點,請把這個組件放到父組件上凳寺,方便組件間通信

3鸭津、私有方法-僅作為渲染view的使用,不許有業(yè)務(wù)邏輯
4肠缨、公有方法【不推薦逆趋,理由同root組件】

業(yè)務(wù)邏輯對象

業(yè)務(wù)邏輯對象由以下元素組成:

1、root組件對象引用-this.root
2晒奕、構(gòu)造器-初始化root對象闻书,初始化私有屬性
3、私有屬性
4脑慧、公有方法-對外提供業(yè)務(wù)邏輯
5魄眉、私有方法-以下劃線開頭,內(nèi)部使用

ps1:通用型組件只要求盡量滿足上述架構(gòu)設(shè)計

通用型組件一般為不包含任何業(yè)務(wù)的純技術(shù)組件闷袒,具有高復(fù)用價值坑律、高定制性、通常不能直接使用需要代碼定制等特點囊骤。

可以說是一個系統(tǒng)的各個基礎(chǔ)零件晃择,比如一個蒙板效果冀值,或者一個模態(tài)彈出框。

架構(gòu)的最終目的是保證系統(tǒng)整體結(jié)構(gòu)良好宫屠,代碼質(zhì)量良好列疗,易于維護。一般編寫通用型組件的人也是經(jīng)驗較為豐富的工程師浪蹂,代碼質(zhì)量會有保證抵栈。而且,作為零件的通用組件的使用場景和生命周期都和普通組件\頁面不同乌逐,所以竭讳,僅要求通用組件編寫盡量滿足架構(gòu)設(shè)計即可。

ps2:view子組件復(fù)用問題

拋出一個問題浙踢,設(shè)計的過程中,子組件是否需要復(fù)用灿渴?子組件是否需要復(fù)用會影響到組件設(shè)計洛波。

需復(fù)用,只暴露props骚露,可以內(nèi)部自行管理state【盡量避免除非業(yè)務(wù)需要】
不需復(fù)用蹬挤,只暴露props,內(nèi)部無state【因為不會單獨使用棘幸,不需要setState來觸發(fā)渲染】
其實焰扳, 一般按照不需復(fù)用的情況設(shè)計,除非復(fù)用很明確误续,但這時候應(yīng)該抽出去吨悍,變成獨立的組件存在就可以了,所以這個問題是不存在的蹋嵌。

適用場景分析

flux系列框架

flux系列框架的適用場景我覺得應(yīng)具有以下特點:

一個頁面中組件較多育瓜,組件之間較為獨立栽烂,但是重疊使用模型,模型的變化會影響很多組件的展現(xiàn)和行為焰手。

比如,開發(fā)一個類似qq的聊天頁面书妻,左側(cè)是聯(lián)系人列表,右側(cè)是與某人的消息對話框驻子,當(dāng)收到一個消息之后灿意,1要刷新左側(cè)聯(lián)系人列表的最近聯(lián)系人缤剧,2要右側(cè)的消息對話框中顯示這個消息,3要頁面title要提示新消息荒辕。這就是典型的一個新消息到來事件【消息模型發(fā)生了變化】觸發(fā)三個無關(guān)聯(lián)的組件都有行為和展現(xiàn)的變化犹褒。如果用事件系統(tǒng)來開發(fā)就做到了解耦的極致,未來如果還要加入第4種處理也不用修改原來的邏輯叠骑,就直接注冊一下就可以了,滿足了開閉原則宙枷。

面向?qū)ο蟮腞N組件架構(gòu)

面向?qū)ο蟮腞N組件架構(gòu)的使用場景特點我沒有總結(jié)出來,我覺得所有場景都可以用慰丛,只要你業(yè)務(wù)邏輯對象設(shè)計的好卓囚,都不是問題。

還拿上面聊天界面舉例子诅病,面向?qū)ο蟮腞N組件架構(gòu)其實也可以解耦的寫出寫上述場景哪亿,你完全可以將業(yè)務(wù)邏輯對象之間的交互設(shè)計成一個小的事件系統(tǒng),只是架構(gòu)沒有直接約束這種解耦贤笆,flux系列架構(gòu)直接在架構(gòu)中就強制編碼人員做到了解耦蝇棉,但是如果我不需要解耦的時候就相當(dāng)于增加了復(fù)雜度,得不償失了苏潜。

所以面向?qū)ο蟮腞N組件架構(gòu)要更靈活银萍,同時因為靈活對業(yè)務(wù)邏輯對象設(shè)計者的要求也較高,針對較為復(fù)雜or重要頁面建議進行詳細(xì)設(shè)計并leader檢查來保證質(zhì)量恤左。

如何做監(jiān)控

因為面向?qū)ο蟮腞N架構(gòu)中去掉了統(tǒng)一的業(yè)務(wù)邏輯調(diào)用facade入口dispatch贴唇,那我們?nèi)绾蝸碜霰O(jiān)控呢。

方案1:在需要監(jiān)控的地方人為加入監(jiān)控點飞袋。

這個方案對業(yè)務(wù)代碼和監(jiān)控代碼的耦合確實有點大戳气,是最差的解決方案了。不推薦巧鸭。

方案2:在基類BaseLogicObj的構(gòu)造器中對對象的所有方法進行代理-todo待驗證

這個方案對業(yè)務(wù)代碼透明署辉,但是還只是個想法瓶埋,未進行代碼測試和驗證敏沉。

完整demo代碼

此demo仿照redux提供的todolist demo編寫。

redux demo 地址:http://camsong.github.io/redux-in-chinese/docs/basics/ExampleTodoList.html

demo截圖:

todolist頁面:

'use strict'


let React=require('react-native');
let Immutable = require('immutable');
var BbtRN=require('../../../bbt-react-native');


var {
    BaseLogicObj,
    }=BbtRN;


let {
    AppRegistry,
    Component,
    StyleSheet,
    Text,
    View,
    Navigator,
    TouchableHighlight,
    TouchableOpacity,
    Platform,
    ListView,
    TextInput,
    ScrollView,
    }=React;

//root組件開始-----------------

let  Root =React.createClass({

    //初始化模擬數(shù)據(jù)贸毕,
    data:[{
        name:'aaaaa',
        completed:true,
    },{
        name:'bbbbb',
        completed:false,
    },{
        name:'ccccc',
        completed:false,
    }
    ,{
        name:'ddddd',
        completed:true,
    }],


    componentWillMount(){

        //初始化業(yè)務(wù)邏輯對象
        this.addTodoObj=new AddTodoObj(this);
        this.todoListObj=new TodoListObj(this);
        this.filterObj=new FilterObj(this);

        //下面可以繼續(xù)做一些組件初始化動作,比如請求數(shù)據(jù)等.
        //當(dāng)然了這些動作最好是業(yè)務(wù)邏輯對象提供的夜赵,這樣root組件將非常干凈.
        //例如這樣:this.todoListObj.queryData();
    },


    //狀態(tài)初始化
    getInitialState(){
      return {
          data:Immutable.fromJS(this.data),//模擬的初始化數(shù)據(jù)
          todoName:'',//新任務(wù)的text
          curFilter:'all',//過濾條件 all no ok
      }
    },



    //這里組合子view組件 并 注冊業(yè)務(wù)邏輯對象提供的方法到各個子view組件上
    render(){

        return (
            <View style={{marginTop:40,flex:1}}>

                <AddTodo todoName={this.state.todoName}
                        changeText={this.addTodoObj.change.bind(this.addTodoObj)}
                         pressAdd={this.addTodoObj.press.bind(this.addTodoObj)} />

                <TodoList todos={this.state.data}
                          onTodoPress={this.todoListObj.pressTodo.bind(this.todoListObj)} />

                <Footer curFilter={this.state.curFilter}
                    onFilterPress={this.filterObj.filter.bind(this.filterObj)} />

            </View>
        );
    },



});






//業(yè)務(wù)邏輯對象開始-------------------------可以使用OO的設(shè)計方式設(shè)計成多個對象

//業(yè)務(wù)邏輯對象要符合命名規(guī)范:以O(shè)bj結(jié)尾
//BaseLogicObj是架構(gòu)提供的基類明棍,里面封裝了構(gòu)造器和一些常用取值函數(shù)
class AddTodoObj extends BaseLogicObj{

    press(){
        if(!this.getState().todoName)return;
        let list=this.getState().data;
        let todo=Immutable.fromJS({name:this.getState().todoName,completed:false,});
        this.setState({data:list.push(todo),todoName:''});
    }

    change(e){
        this.setState({todoName:e.nativeEvent.text});
    }

}


class TodoListObj extends BaseLogicObj {




    pressTodo(todo){

        let data=this.getState().data;

        let i=data.indexOf(todo);

        let todo2=todo.set('completed',!todo.get('completed'));

        this.setState({data:data.set(i,todo2)});
    }
}


class FilterObj extends BaseLogicObj {


    filter(type){

        let data=this.getState().data.toJS();
        if(type=='all'){
            data.map((todo)=>{
                todo.show=true;
            });
        }else if(type=='no'){
            data.map((todo)=>{
                if(todo.completed)todo.show=false;
                else todo.show=true;
             });
        }else if(type=='ok'){
            data.map((todo)=>{
                if(todo.completed)todo.show=true;
                else todo.show=false;
            });
        }


        this.setState({curFilter:type,data:Immutable.fromJS(data)});
    }



}


//view子組件開始---------------------------


//子view對象中僅僅關(guān)注:從this.props轉(zhuǎn)化成view
let Footer=React.createClass({

    render(){

        return (


            <View style={{flexDirection:'row', justifyContent:'flex-end',marginBottom:10,}}>

                <FooterBtn {...this.props} title='全部' name='all'  cur={this.props.curFilter=='all'?true:false} />
                <FooterBtn {...this.props} title='未完成' name='no' cur={this.props.curFilter=='no'?true:false} />
                <FooterBtn {...this.props} title='已完成' name='ok' cur={this.props.curFilter=='ok'?true:false} />

            </View>



        );
    },


});


let FooterBtn=React.createClass({

    render(){

        return (

            <TouchableOpacity onPress={()=>this.props.onFilterPress(this.props.name)}
                              style={[{padding:10,marginRight:10},this.props.cur?{backgroundColor:'green'}:null]} >
                <Text style={[this.props.cur?{color:'fff'}:null]}>
                    {this.props.title}
                </Text>
            </TouchableOpacity>

        );
    },


});


let AddTodo=React.createClass({

    render(){

        return (


            <View style={{flexDirection:'row', alignItems:'center'}}>


                <TextInput value={this.props.todoName}
                    onChange={this.props.changeText}
                    style={{width:200,height:40,borderWidth:1,borderColor:'e5e5e5',margin:10,}}></TextInput>


                <TouchableOpacity onPress={this.props.pressAdd}
                    style={{backgroundColor:'green',padding:10}} >
                    <Text style={{color:'fff'}} >
                        添加任務(wù)
                    </Text>
                </TouchableOpacity>

            </View>



        );
    },


});



let Todo=React.createClass({

    render(){
        let todo=this.props.todo;
        return (
            todo.get("show")!=false?
            <TouchableOpacity  onPress={()=>this.props.onTodoPress(todo)}
                style={{padding:10,borderBottomWidth:1,borderBottomColor:'#e5e5e5'}}>
                <Text style={[todo.get('completed')==true?{textDecorationLine:'line-through',color:'#999'}:null]} >
                    {todo.get('completed')==true?'已完成   ':'未完成   '} {todo.get('name')}
                </Text>
            </TouchableOpacity>
             :null
        );
    },


});


let TodoList=React.createClass({
    render(){
        return (
            <ScrollView style={{flex:1}}>
                {this.props.todos.reverse().map((todo, index) => <Todo {...this.props} todo={todo} key={index}  />)}
            </ScrollView>
        );
    },
});




module.exports=Root;

業(yè)務(wù)邏輯對象基類BaseLogicObj:

'use strict'

class BaseLogicObj{


    constructor(root){
        if(!root){
            console.error('實例化BaseLogicObj必須傳入root組件對象.');
        }
        this.root=root;
    }

    getState(){
        return this.root.state;
    }

    setState(s){
        this.root.setState(s);
    }

    getRefs(){
        return this.root.refs;
    }

    getProps(){
        return this.root.props;
    }

}

module.exports=BaseLogicObj;

參考:http://react-china.org/t/reactnative/3486

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市寇僧,隨后出現(xiàn)的幾起案子摊腋,更是在濱河造成了極大的恐慌兴蒸,老刑警劉巖橙凳,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痕惋,死亡現(xiàn)場離奇詭異娃殖,居然都是意外死亡炉爆,警方通過查閱死者的電腦和手機芬首,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門郁稍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耀怜,“玉大人财破,你說我怎么就攤上這事左痢。” “怎么了略步?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵趟薄,是天一觀的道長竟趾。 經(jīng)常有香客問我岔帽,道長犀勒,這世上最難降的妖魔是什么妥曲? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任褂萧,我火速辦了婚禮葵萎,結(jié)果婚禮上羡忘,老公的妹妹穿的比我還像新娘。我一直安慰自己节猿,他們只是感情好滨嘱,可當(dāng)我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布九孩。 她就那樣靜靜地躺著躺彬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪仿野。 梳的紋絲不亂的頭發(fā)上她君,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天球涛,我揣著相機與錄音校镐,去河邊找鬼鸟廓。 笑死,一個胖子當(dāng)著我的面吹牛牍陌,可吹牛的內(nèi)容都是我干的毒涧。 我是一名探鬼主播贝室,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼茫藏!你這毒婦竟也來了务傲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤看杭,失蹤者是張志新(化名)和其女友劉穎楼雹,沒想到半個月后贮缅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谴供,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡桂肌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年佩耳,在試婚紗的時候發(fā)現(xiàn)自己被綠了照雁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖污呼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情籍凝,我是刑警寧澤饵蒂,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布退盯,位于F島的核電站渊迁,受9級特大地震影響灶挟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜箱叁,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一蝌蹂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧剃允,春花似錦斥废、人聲如沸给郊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至逾雄,卻和暖如春鸦泳,著一層夾襖步出監(jiān)牢的瞬間永品,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工誊垢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谋作。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓遵蚜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親睡汹。 傳聞我的和親對象是個殘疾皇子寂殉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容