React Native开发小结

1、函数式组件

函数式组件即通过调用一个方法返回一个组件,可以解决组件开发时嵌套过多组件代码臃肿问题。

render() {        
    return(
        <View style={styles.content}>
           {this._renderSectionHeader()}
           {this._renderSections()}
           {this._renderSectionFooter()}
        </View>
    )
}

将一个组件进行拆分,拆分到多个函数里边,使代码简洁,直观,便于维护

2、获取组件frame

iOS开发中,一个view的frame可以用(x,y,width,height)来设定,这四个属性一旦确定,这个组件的在什么位置怎么显示就固定了。并且想要获取组件的x、y、width、height也很方便,比如view.frame.origin.x,view.frame.size.width。但是在React Native开发中如果想要使用组件当前状态的x、y、width、height就要用下边的回调的方式拿到。

<View 
    onLayout={this._onLayout}
>
</View>

_onLayout(event){
   let {x,y,width,height} = e.nativeEvent.layout
   ...
}

3、gif图片使用

iOS端React Native加载gif图片使用source方式不起作用,换为require方式,使用相对路径加载gif图片即可。

var gifImage = require('./../assets/rn_snatch_order.gif')
<Image 
    style={styles.image}
    source={gifImage}
/>  

4、组件的隐藏和显示

组件的显示和隐藏在iOS中非常简单,如下设置即可

// 方案一
view.hidden = YES;  //YES or NO
// 方案二
view.height = 0

React Native中由于组件没有hidden属性,所以需要另觅他途,目前我常用的有两种方式。

4.1 设置state属性

设置state后当前组件刷新,根据state属性值来判断当前组件是否加载可隐藏组件,这是最常用的方法。如果不需要显示组件,将原来return的组件设置为return null即可。

constructor(props){
    super(props)
    this.state = {
        isShowCountDown:false
    }
 }
    
_renderHeaderActivityLabel() {
    return(
       <LinearGradient>
           <View>
               {this.state.isShowCountDown ? <View style={styles.countDownStyle}>
                    ...
               </View> : null}
            </View>
       </LinearGradient>
    )
}

但是这种方法有弊端,就是在组件隐藏时相当于没有加载这个组件,如果你想要使用组件中的某个数据或属性,那么抱歉,你拿到的是undefined。我在开发中就遇到了这种问题,于是寻求到了第二种方案。

4.2 设置height

iOS中可以通过设置view.height = 0来使组件不显示,那么React Native可以可以呢,答案是肯定的。组件加载的时候,设置style = {{height:0}}即可。不过要注意,仅仅这么做是不够的的,设置了组件的高度为0并不代表其子组件的高度也为0,如果子组件高度不为0,那么子组件还是会显示出来,我们可以通过设置overflow属性来解决这个问题,设置后超出当前组件的所有子组件都会被隐藏掉。

viewStyle:{
    height:0,
    overflow:'hidden'
}
reactnative_0.png

还有一个问题,现在设置了组件的高度为0,要显示的时候怎么办呢,在组件重新渲染的时候将组件设置为需要的高度即可,当然这个组件这个时候是一个固定高度。如果可变高度的,可使用上边第二条获取组件的layout,用一个属性记录下来,刷新的时候设置组件高度,代码如下:

constructor(props){
    super(props)
    this.height = 0;
}
 
_renderScrollerView (){
    let normalStyle = {height:0,overflow:'hidden'}
    let selectStyle = {height:this.height}
    return(
        <ScrollView
            onLayout={this._onLayout}
            style={this.state.hightLight ? selectStyle : normalStyle}
            ...
        >
            {this._renderSections()}
        </ScrollView>
    )
}

_onLayout(event){
   this.height = event.nativeEvent.layout.height
}

或者不用onLayout,使用let selectStyle = {},不设置height,使用约束布局,使组件自适应高度也可以完成高度变化。

reactnative_1.png

4.3 设置display

React Native 0.47版本开始支持display属性,可用于组件的显示和隐藏。代码如下:

var hiddenStyle = null;
if(hidden) hiddenStyle = {display:'none'}
<view style = {[{width:10,height:10},hiddenStyle]}>
    ...
</View>

5、Text控件

5.1 文本居中

Text控件的文本设置justifyContent:'center'后只能水平居中,如果想要垂直居中,两个方法,第一个设置line-height;第二个方法外边再包一层View组件,然后设置justifyContent:'center'

// 方法一
<Text style={height:100,lineHeight:100}>
    {text}
</Text>

// 方法二
<View
    style={{flex:1,justifyContent:'center',alignItems:'center'}}
>
    <Text>
        {text}
    </Text>
</View>

5.2 富文本

reactnative_3.png

在iOS中如果想要实现下面的文本样式还是比较麻烦的,首先我需要知道特殊文本所在的位置和长度,然后针对这块区域去设置相关属性,而React Native使用文本Text嵌套的方式就能很方便的实现

<Text style={{paddingLeft:global.local == 'xx' ? 5 : 0,fontSize:12,color:'#999999'}}>
    {protectText}
    <Text
        style={{fontSize:12,color:'#FF6702'}}
        onPress={()=>{
            this._loadWebView(protectUrl);
        }}
    >
        {global.local == 'xx' ? text : ''}
    </Text>
</Text>

6、组件间通信

6.1 父组件-->子组件

父组件以props的方式将属性传递给子组件,子组件通过this.props.xxx拿到父组件属性值。这种情况适用于变量或者方法的传递。如果希望组件接收到的数据类型是有效的,可以通过PropTypes进行检查,此时如果传入的值类型不正确,在开发者模式下会给出warning提示,方便我们进行调试和修复问题。

static propTypes = {
    onSelected:PropTypes.func,
    name:PropTypes.string,
    digitStyle: PropTypes.object,
    timeToShow: PropTypes.array,
    showSeparator: PropTypes.bool,
    size: PropTypes.number
}

6.2 子组件-->父组件

this.props方式

这里可以使用6.1中的props方式传递,将function作为属性传递给子组件,子组件在适当时机通过this.props回调function将值传递回父组件。

_renderHotBuyViewSelected() {
    ...
    return(
        <View style = {[styles.cover,topStyle]}>
            <HotBuyView
                ...
                addCart={(token)=>{this.buy(token)}}
            />
        </View>
    )
}

buy(token){
    ...
}

最后在子组件中的适当时机,执行this.props.addCart(token)即可将token传递回父组件使用。

ref方式

ref可以看做是组件被渲染后,指向该组件的一个引用,我们可以通过ref来获取到该组件的实例,然后就可以使用该组件的public属性和方法。

_renderProductSuit(){
    return(
        <ProductSuit
            ...
            ref = {(ref)=>this._suitView = ref}
        />
    )
}

如代码所示,我们通过当前组件的this._suitView保存了ref所指向的ProductSuit实例,后面我们就可以通过this._suitView使用ProductSuit组件的public属性和方法。

③ 通知

对于没有相互关联关系或者层级过深的组件间通讯可以考虑使用通知的方式来完成。

// 被通知组件
componentDidMount() {
    this.closePorductService = DeviceEventEmitter.addListener('closePorductService',
        () => {
            this._hiddenAction();
        }
    );
}
    
componentWillUnmount() {
    this.closePorductService.remove();
}

_hiddenAction() {
    ... 
}

在需要发出通知的组件中的适当时机调用DeviceEventEmitter.emit('closePorductService')发出通知,被通知组件在加载完毕后会接收到通知,执行相应的操作,在组件被卸载的时候记得要移除对应的通知。

7、组件生命周期

reactnative_4.png

从图中可以看出,React Native的生命周期大致分为三部分:

  1. 实例化阶段:如图中最上面部分,是组件第一次绘制阶段,在这个阶段组件完成了初始化,渲染和加载。
  2. 运行阶段:如图中左下面部分,是组件的运行和交互阶段,在这个阶段组件完成了用户交互处理,接收事件,根据state重新渲染界面。
  3. 销毁阶段:如图中右下面部分,是组件的卸载销毁阶段,在这里做一些组件的清理工作,如取消计时器、网络请求等。

注意:

render()方法中不要初始化或设置组件的state,因为它会导致当前组件重新渲染,并引发警告或者错误,如果有setState()需求,更好的方法是在componentDidMount()方法内部完成设置操作。

8、引入第三方库

目前React Native自身和一些第三方的库都是用了npmyarn来管理和托管代码,这里我们以yarn为例。

8.1 依赖库不包含原生代码

安装依赖库

yarn add xxx

链接依赖库

react-native link     //链接所有依赖库
react-native link xxx //只连接某一个库时请使用这个

8.2 依赖库包含原生代码

一般的依赖库完成这些操作就可以通过import导入使用了,但是如果依赖库包含了原生代码实现,那么需要额外的操作把这些文件添加到你的应用,否则应用会在你使用这些库的时候产生报错。我们介绍两种方式,一种是手动链接(不建议使用,坑比较多,也比较麻烦,特别是集成了CocoaPods的项目);另一种是自动链接。

手动链接

这里不做过多介绍,如有兴趣,请参考React Native中文网相关介绍(https://reactnative.cn/docs/0.51/linking-libraries-ios/)。

自动链接

项目使用了CocoaPods就非常方便了,react-native link xxx链接依赖库这一步就可以直接跳过了,然后参照依赖库的官方安装文档或者参考下面步骤来完成安装链接即可。

以当前MIStore项目使用的RN第三方库react-native-i18n为例,使用yarn安装依赖库

yarn add react-native-i18n

通过CocoaPods自动安装,将下面代码添加到Podfile文件,执行pod install即可。

pod 'RNI18n', :path => '../node_modules/react-native-i18n'

目前几乎所有的第三方React Native库都支持Pod的方式引入。

9、Modal组件遮挡原生页面问题

reactnative_5.png

iOS原生View有着不同的优先级,优先级最高的显示在最前面,优先级低的则因为在后面而被遮挡。React Native页面的弹窗遮罩或蒙层如果想要遮挡native界面的导航栏,则需要使用Modal这种组件,Modal组件能够用来覆盖包含React Native根视图的原生视图(如UIViewController,Activity)。如图所示在Modal组件页面点击链接跳转的native页面也被覆盖遮住了。

既然Modal的优先级很高,以至于native页面都被遮盖住了,那么解决问题的思路就是能不能跳转Web页面的时候隐藏掉弹窗组件,关闭Web页面的时候再把弹窗组件显示出来呢,根据这个思路,设计了以下解决方案,最终解决了遮挡问题。

加载Web页面时发出通知,关闭当前弹窗组件,并且给native通信,给当前的Web页面增加标识protectOpenWebView,后面关闭Web页面是再根据标识做相应的处理操作。

_loadWebView(url) {
    if(url == null || url.length == 0 || url.indexOf('://') == -1) return;
    nativeVC.push(url);
    DeviceEventEmitter.emit('closePorductSelected');
    nativeVC.rnOpenWeb('protectOpenWebView');
}

native的Web页面关闭时,native和React Native通信,发送携带protectOpenWebView标识的信息,React Native收到后判断标识来展示对应的弹窗

componentDidMount() {
    this.openSubscription = NativeAppEventEmitter.addListener('EventReminder',
        (reminder) => {
            let identify = reminder.identify;
            if(identify && identify == 'protectOpenWebView') {
                this._showActionSheet(ActionSheetType.ActionSheetTypeProductSelected);
            }
            nativeVC.rnOpenWeb('');//重置标识
        }
    );
    this.closePorductSelected = DeviceEventEmitter.addListener('closePorductSelected',
        () => {
            this._hiddenAction();
        }
    );
}
componentWillUnmount() {
    this.openSubscription.remove();
    this.closePorductSelected.remove();
}

10、React Native获取native变量

10.1 回调

一般情况下,在加载React Native页面的时候可以通过传值的方式把需要的参数和值传递过来,但是有些初始化操作在每个js文件加载的时候就开始做了,这个时候是获取不到传递进来的参数值的,比如下面代码:

var regionCode = global.local;  //执行后regionCode为null
if(regionCode == 'in') {
    I18n.locale = 'en';
} else if(regionCode == 'hk') {
    I18n.locale = 'zh_HK';
} else if(regionCode == 'tw') {
    I18n.locale = 'zh_TW';
}

所以说务必使用同步方法在拿到native的国家code后直接给I18n.locale赋值,因此这里使用了回调的方式,在加载React Native代码的时候去和native通信,拿到国家code后通过回调方式传回来直接赋值。

React Native代码

nativeModule.getAppRegionCode((regionCode)=>{
    if(regionCode == 'in') {
        I18n.locale = 'en';
    } else if(regionCode == 'hk') {
        I18n.locale = 'zh_HK';
    } else if(regionCode == 'tw') {
        I18n.locale = 'zh_TW';
    }
})

native代码

RCT_EXPORT_METHOD(getAppRegionCode:(RCTResponseSenderBlock)callBack){
    if(callBack){
        callBack(@[[MIAppContext sharedInstance].currentRegion.regionCode]);
    }
}

10.2 导出常量

Native模块可以在运行时向React Native导出立即可用的常量,这是非常有用的一个功能,可以不用回调的方式来及时拿到所要使用的数据,避免了这些数据通过bridge来回传递。

- (NSDictionary *)constantsToExport {
    return @{@"regionCode": [MIAppContext sharedInstance].currentRegion.regionCode};
}

在React Native模块里边直接使用下面代码即可拿到数据赋值使用:

I18n.locale = nativeVC. regionCode;

不过要注意的是在初始化React Native模块时常量才会被导入,如果运行时改变了constantsToExport代码块的常量值,它不会影响React Native模块的常量值。如果想要改变React Native模块的导入常量的值,只能重新加载React Native模块,再次导入常量值使用。

参考

React Native 中文网
React Native 生命周期函数详解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353