React-Native开发总结-react层面上的问题

最近更新时间:2017年10月30日11:43:49

《我的博客地图》

    在实际开发中,如果采用框架进行项目构建,组件的生命周期是重头戏,也是难点和重点。学习了理论知识点需要深度实践才能领悟其中的意思。

1、react的生命周期

如下图所示:

生命周期方法执行步骤:constructor-componentWillMount-render-componentDidMount

 

①实例化阶段:Mounting

a.getDefaultProps(){}

ES6子组件语法形式为A.defaultProps={},整个生命周期执行一次;

b.getInitialState(){}

ES6组件语法形式为this.state={},整个生命周期执行一次;

c.componentWillMount(){}

before render-在页面渲染之前执行的方法,整个生命周期执行一次;

d.render(){}

渲染函数,返回DOM结构,整个生命周期会多次执行;

e.componentDidMount(){}

after render-在页面首次渲染之后执行的方法,整个生命周期执行一次;

注意:挂载阶段,重新渲染阶段的abcde方法都会执行一遍!

 

②重新渲染阶段:Updating

a.componentWillReceiveProps(nextProps){}

props changed-页面属性发生变化后执行的方法;在这个函数中,可以根据属性的变化调用setState来更新组件状态,这里调用setState更新状态是安全的,并不会触发额外的render方法;props是父组件传递给子组件的。父组件发生render的时候子组件就会调用componentWillReceiveProps(不管props有没有更新,也不管父子组件之间有没有数据交换)。

新解:在父子组件通信时,这个方法很重要,子组件通过this.props.data拿到父组件传递过来的数据,如果是UI层面的显示类型数据,这就代表子组件中的数据是props类型,props发生变化不会触发子组件的render方法,因此需要把父组件传递过来的数据拿过来后,在子组件中当做state使用,这样在这个方法中执行setState,触发子组件的render方法。

什么时候用?

当子组件中的state某一个值使用了父组件传递过来的数据,父组件传过来的props发生变化,子组件并不会渲染,这时候就需要用手动触发子组件重新渲染,如下:
componentWillReceiveProps(nextProps){
  if(nextProps.data !== this.props.data){
    this.setState({
      data: nextProps.data
    });
  }
}

b.shouldComponentUpdate(nextProps, nextState){}

当props或state发生变化时,父组件发生render的时候子组件就会调用它,在render方法之前执行,如果这个方法return true,则执行render方法,如果这个方法return false,则不执行render方法;用来判断是否需要更新组件;调用forceUpdata()也不会执行;默认返回true,防止state快速变化的问题;如果返回false,render()、componentWillUpdate()和componentDidUpdate()这三个方法都不会执行;使用场景,当组件较多时,需要重写这个方法,决定是否重新渲染子组件,提高应用的性能;组件挂载之后,每次调用setState后都会调用shouldComponentUpdate判断是否需要重新渲染组件。默认返回true,需要重新render。在比较复杂的应用里,有一些数据的改变并不影响界面展示,可以在这里做判断,优化渲染效率。

新解:做性能优化操作,在每个子组件中重写这个方法如下:

shouldComponentUpdate(nextProps, nextState){
  if(nextProps.data == this.props.data || nextState.data == this.state.data){
    return false
  }
  return true
}

但在每个组件写比较麻烦,可以使用第三方组件react-addons-pure-render-mixin来操作如下:

import PureRenderMixin from 'react-addons-pure-render-mixin';

constructor(props) {
  super(props);
  this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
}

c.void componentWillUpdate(){object nextProps, object nextState}

更新渲染前调用;shouldComponentUpdata返回true,在render方法之前执行;但初始化render不执行这个方法;这个方法的作用,把nextProps和nextState的值分别设置到this.props和this.state中;shouldComponentUpdate返回true或者调用forceUpdate之后,componentWillUpdate会被调用。

d.render()

e.void conponentDidUpdata(){object nextProps, object nextState}

更新渲染后调用;shouldComponentUpdata返回true,在render方法之后执行;但初始化render不执行这个方法;除了首次render之后调用componentDidMount,其它render结束之后都是调用componentDidUpdate。

 

③销毁:Unmounting

a.componentWillUnMount(){}

leave view,页面销毁才执行;组件被卸载的时候调用,一般在componentDidMount里面注册的事件需要在这里删除。

但是this.props.navigation.navigate()不会触发这个方法,只有this.props.navigation.goBack()才会触发这个方法,因为navigate向路由栈push数据,goBack向路由栈pop数据;

 

④异常处理:Error Handling

a.componentDidCatch(){}

在页面渲染(任何生命周期方法、构造函数或者子组件执行)过程中,一旦出错会执行这个方法;

 

注意事项:只在componentWillMount,componentDidMount,componentWillReceiveProps方法中修改state的值;不能在render中修改state

 

其他方法:replaceState()、forceUpdata()

 

2、生命周期解读

    生命周期方法执行的步骤:

constructor-componentWillMount-render-componentDidMount-componentWillUpdate-render-componentDidUpdate

    触发render方法(重新渲染UI界面)执行的四种情况:

a.首次渲染;

b.触发setState方法(并不是一次setState会触发一次render,React可能会合并操作,再一次性进行render);

c.父组件发生更新(一般就是props发生改变,但是就算props没有改变或者父子组件之间没有数据交换也会触发render);父组件中,接收redux新的props数据(比如redux的数据发生改变;直接触发UI界面重绘)

d.调用this.forceUpdate();

    注意事项:官方文档,从后台获取数据一定要放在componentDidMount中调用,不能放在constructor或componentWillMount中:会阻碍组件的实例化,阻碍组件的渲染;如果用setState,在componentWillMount里面触发setState不会重新渲染

3、组件无限次执行初始化方法

    组件生命周期方法执行的步骤:constructor-componentWillMount-render-componentDidMount,但实际出现的问题是无限循环执行constructor-componentWillMount-render

查找原因,render方法出错,render中的子组件需要的数据this.props.data为空;

排查过程:使用componentDidCatch方法捕捉异常

4、react组件化编程详解,父子组件通信

父组件:

import Children from './Children/index.js'

 

export default class Parent extends Component {

  constructor(props) {
    super(props);

    this.state = {name:''}
  }

  render(){

    return(<View>

    <Children

      propsA={}

      propsB={}

      propsC={}

      propsD={}

      name={this.state.name}

      ...

      </View>

  )}

}

    父组件Parent中引用了子组件Children,子组件Children的属性可以任意写(数量和名称),在子组件中取值有两种方法,第一,this.props.name、this.props.propsA;第二,在componentWillReceiveProps方法中通过形参nextProps也可以取到,nextProps.name、nextProps.propsA;

 

子组件:

 

export default class Children extends Component {

  constructor(props) {
    super(props);
    this.state = {
      name: ''
    }
  }
  componentWillReceiveProps(nextProps){
      this.setState({propsA: nextProps.propsA});
  }

  static defaultProps = {

        propsA: 'wanshaobo'
    }
  static propTypes = {
        propsA: React.PropTypes.string.isRequired
  };

  render(){

    return(<View><Text>{this.props.propsA}</Text><Text>{this.state.name}</Text></View>)

  }

}

    子组件中第一个方法:defaultProps 给对象设置默认属性,如果传来的对象没有对应值,则把这里的属性赋值给该对象。第二个方法:propTypes 设置props的类型,如上:propsA规定为string类型,isRequeired是说明该值不能为空,必须传递,如果为空,会有个提示,当然,这个提示在debug模式才会有,方便开发,如果没有第一个方法,那么不传会有错误提示,有的话会直接赋值给空对象。设置了这两个,就不会有空值了。

    因此,子组件的数据需要做为空判断,或者初始化默认值;自定义初始化默认属性的方法defaultProps和propTypes,设定默认属性和属性类型;

    componentWillReceiveProps方法的用途:如果子组件的数据需要动态改变,在父组件中改变子组件的数据,子组件不会重新渲染,触发子组件重新渲染的唯一方法是setState;

    子组件的默认属性和属性类型的写法也可以如下:

 

Children.defaultProps = {

    propsA: 'wanshaobo'
}
Children.propTypes = {
    propsA: React.PropTypes.string.isRequired

};

5、获取图片尺寸的正确方法

componentDidMount(){

window.onload = ()=>{

let imgDom = findDomNode(this.refs.img01);

let imgWidth = imgDom.scrollWidth;

}

}

6、ref精讲

    获取Text元素的内容:

<Text ref={'resendText'}>{data.data}</Text>

let text = this.refs.resendText.props.children

    获取组件实例

<FlatList ref={ref => {this._flatlist = ref}} />

7、渲染元素方法

    方案一:

render(){

return <View style={{}}><Text style={{}}>文字</Text></View>

}

    方案二:

import React from 'react';

import ReactNative from 'react-native';

 

render(){

return React.createElement(ReactNative.View,{style:{}},React.createElement(ReactNative.Text,{style:{}},文字))

}

8、组件性能优化-对象(props和state)浅比较

import PureRenderMixin from 'react-addons-pure-render-mixin';

constructor(props){

super(props);

this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);

}

    在底层,该插件实现了shouldComponentUpdate,在这里面,它比较当前的props、state和接下来的props、state,当两者相等的时候返回false,不进行更新。

    前提:React组件的渲染函数是一个纯函数也就是说对于相同的值返回一样的结果同时不影响元素局,在某些场景下,你可以利用这个插件来极大地提升性能。

function(a){return a} 纯函数

function(a){return Math.random()+a} 非纯函数

    注意:该插件仅仅是浅比较对象。如果对象包含了复杂的数据结构,深层次的差异可能会产生误判。仅用于拥有简单props和state的组件,或者当你知道很深的数据结构已经变化了的时候使用forceUpdate()。或者,考虑使用immutable objects(不变数据)来帮助嵌套数据快速比较。 
[shouldComponentUpdate会跳过更新整个组件子树。确保所有的子组件也是“纯粹的”]这套路不多说。

 

8、父子组件

    数据层面的问题,数据的唯一入口在父组件,子组件通过this.props.data获取数据;

    UI层面,子组件状态子组件自己维护;

9、虚拟DOM Diff算法

    参考:http://www.infoq.com/cn/articles/react-dom-diff

 

 

 

未完,待续...

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值