今天來寫一個組件,相信很多人都會用到的——ViewStack。
ViewStack組件無疑是UI中很重要的一個組件,可惜react-native並沒有內嵌進去,需要開發者自己去實現。
實現原理很簡單,就是根據索引來顯示一個子視圖,用一個render即可完成:
1 render(){ 2 return( 3 <View> 4 {this.props.children[this.props.index]} 5 </View> 6 ); 7 }
這樣,一個最簡單的ViewStack就完成了,那怎么使用呢:
1 <ViewStack index={this.state.tabIndex}> 2 <PageSNS/> 3 <PageGroup/> 4 <PageLibrary/> 5 <PageChat/> 6 <PageProfile/> 7 </ViewStack>
通過修改state的tabIndex來切換子視圖即可。
是不是挺簡單呢!但是,你用着用着就會發現,這有點問題,當你來回切換視圖的時候,會發現,子視圖的狀態每次都重置了,比如在PageSNS視圖上,你滾動列表到了下面,然后切換到了其他子視圖,再切換回來,會發現列表又從回到頂部了。
那么問題來了,有些開發者就想要這樣的體驗,那沒事。而有些需求確是必須要保留狀態,那怎么整呢?
為什么子視圖會被重置呢?那就要涉及到react的渲染機制了,它會再render的時候遍歷一次所有子節點,把要卸載的都卸載掉,要裝載的給裝載上去,那就一目了然了。我們的簡單版ViewStack僅僅是每次渲染一個子視圖,而其他的時候會被卸載掉,當要重新渲染那個視圖的時候,那個視圖其實已經不在了,在只是新new出來(或者從對象池里拿出來並初始化后)的那個視圖了,所以這就是問題的所在了。
那么怎么保留子視圖的狀態呢?(這個狀態並不是react的state機制,而說得是整個視圖的邏輯狀態,好吧,我也扯不清楚了)
我做了一些測試,發現只要組件被卸載了,那么這個組件就不可能恢復了,或者說恢復代價有點高。當然,方法是有的,而且很簡單,只是這種方法比較魯莽,聽我道來。
在使用Navigator組件的時候,會發現,他的子視圖怎么能保留狀態呢,這個很神奇,難道他把什么引用存起來了?然后我深入Navigator的源碼看了下去,發現他坑爹把全部子視圖都render了,把要顯示的視圖給顯示,而不需要顯示的則移到了屏幕外面,就這么簡單啦,我也懵了,原來就是這種方法,相信你也會說一句靠之類的感嘆詞吧,但是它的確就是這么做的,所以我說這種方法有些魯莽。
然后我就寫下了2.0版的ViewStack:
1 /** 2 * Created by rockyl on 15/11/08. 3 */ 4 var React = require('react-native'); 5 var { 6 StyleSheet, 7 Component, 8 View, 9 } = React; 10 var Dimensions = require('Dimensions'); 11 var SCREEN_WIDTH = Dimensions.get('window').width; 12 var SCREEN_HEIGHT = Dimensions.get('window').height; 13 14 class ViewStack extends Component { 15 constructor(props) { 16 super(props); 17 } 18 19 static get defaultProps(){ 20 return { 21 index: 0, 22 } 23 }; 24 25 render(){ 26 this.views = this.props.children.map((page, i)=>{ 27 var style = this.props.index == i ? [styles.viewBase] : [styles.viewBase, styles.viewDisabled]; 28 return ( 29 <View 30 key={'view_' + i} 31 style={style}> 32 {page} 33 </View> 34 ); 35 }); 36 return ( 37 <View style={[styles.container, this.props.style,]}> 38 {this.views} 39 </View> 40 ); 41 } 42 } 43 44 var styles = StyleSheet.create({ 45 container: { 46 flex: 1, 47 overflow: 'hidden', 48 }, 49 viewBase: { 50 position: 'absolute', 51 overflow: 'hidden', 52 left: 0, 53 right: 0, 54 bottom: 0, 55 top: 0, 56 }, 57 viewDisabled: { 58 top: SCREEN_HEIGHT, 59 bottom: -SCREEN_HEIGHT, 60 }, 61 }); 62 63 module.exports = ViewStack;
so easy!
用法和簡單版是一樣的。
再然后,你用着用着就會發現,這切換的時候好生硬啊,秒切,略缺少點什么用戶體驗,比如說滾動之類的動畫也要啊。
2.1版將攜帶動畫參數和其他高級功能參數,那就要等這篇博文更新啦!
