Props和State比较

props

props是properties的缩写,您可以把任意类型的数据传递给子组件,如此子组件的默认数据源就是props。

你可以在挂在组件的时候设置它的props:

let data = {name : xxx};

<
Component data = {data} /
>

在使用中可以通过this.props.data调用props属性,但是切记能用this.props.xxx修改属性,这会导致props错乱。如:

this.props.data = {name:'nextxxxx'}
// 这是错误的示范,切记不要修改props及其子属性,可以把props当做const类型来看待。
  • 设置默认值
static defaultProps = {
    name:'CRN'
};
  • 属性检查
static propTypes = {
    name:React.PropTypes.string
};

state

它是组件的内部状态属性,主要用来存储组件自身需要的数据。 除了初始化时可能由props来决定,之后就完全由组件自身去维护。 组件中由系统定义了setState方法,每次调用setState时都会更新组件的状态,触发render方法重新渲染界面。 需要注意的是render方法是被异步调用的,这可以保证同步的多个setState方法只会触发一次render,这样做是有利于提高性能的。

  • 设置默认值
    constructor(props){
        super(props)
        this.state = {
            showName:'CRN'
        }
        //切记,在此处不要使用setState()
    }
  • 刷新页面状态
componmentWillMount(){
    this.setState({
        showName:'RN'
    })
}

提到state就不得不提组件的生命周期

组件生命周期

  • 创建阶段

该阶段主要发生在创建组件类的时候,在这个阶段中会初始化组件的属性类型和默认属性

static defaultProps = {
        name: ..,
};  // 注意这里有分号
static propTypes = {
        name: React.PropTypes.string.isRequired,
};  // 注意这里有分号
  • 实例化阶段

该阶段主要发生在组件类被调用(实例化)的时候。

1、constructor 这里是对控件的一些状态进行初始化,由于该函数不同于defaultProps,在以后的过程中,会再次调用,所以可以将控制控件的状态的一些变量放在这里初始化.

    constructor(props){
        super(props)
        this.state = {
            showName:'CRN'
        }
        //切记,在此处不要使用setState()
    }

2、componentWillMount()

准备加载组件。 这个调用时机是在组件创建,并初始化了状态之后,在第一次绘制 render() 之前。可以在这里做一些业务初始化操作,也可以设置组件状态。这个函数在整个生命周期中只被调用一次。 如果在这个函数里面调用setState,本次的render函数可以看到更新后的state,并且只渲染一次。

componmentWillMount(){
    this.setState({
        showName:'RN'
    })
}

3、render()

render是一个组件必须有的方法,形式为一个函数,渲染界面,并返回JSX或其他组件来构成DOM,和Android的XML布局、WPF的XAML布局类似,只能返回一个顶级元素。

4、componentDidMount()

调用了render方法后,组件加载成功并被成功渲染出来以后所执行的hook函数,一般会将网络请求等加载数据的操作,放在这个函数里进行,来保证不会出现UI上的错误。

  • 运行(更新)阶段

1、componentWillReceiveProps(nextProps)

当组件接收到新的props时,会触发该函数。在该函数中,通常可以调用setState()来完成对state的修改。 输入参数 nextProps 是即将被设置的属性,旧的属性还是可以通过 this.props 来获取。在这个回调函数里面,你可以根据属性的变化,通过调用 this.setState() 来更新你的组件状态,这里调用更新状态是安全的,并不会触发额外的 render() 调用

componentWillReceiveProps: function(nextProps) {  
  this.setState({
    showName: nextProps.ShowName
  });
}

2、shouldComponentUpdate(nextProps, nextState)

返回布尔值(决定是否需要更新组件)

输入参数 nextProps 和上面的 componentWillReceiveProps 函数一样,nextState 表示组件即将更新的状态值。这个函数的返回值决定是否需要更新组件,如果 true 表示需要更新,继续走后面的更新流程。否者,则不更新,直接进入等待状态。 默认情况下,这个函数永远返回 true 用来保证数据变化的时候 UI 能够同步更新。在大型项目中,你可以自己重载这个函数,通过检查变化前后属性和状态,来决定 UI 是否需要更新,能有效提高应用性能。

3、componentWillUpdate(nextProps, nextState)

shouldComponentUpdate返回true或者调用forceUpdate之后,就会开始准更新组件,并调用 componentWillUpdate()。 输入参数与 shouldComponentUpdate 一样,在这个回调中,可以做一些在更新界面之前要做的事情。需要特别注意的是,在这个函数里面,你就不能使用 this.setState 来修改状态。这个函数调用之后,就会把 nextProps 和 nextState 分别设置到 this.props 和 this.state 中。紧接着这个函数,就会调用 render() 来更新界面了。

4、render()

再确定需要更新组件时,调用render,根据diff算法,渲染界面,生成需要更新的虚拟DOM数据。

5、componentDidUpdate()

虚拟DOM同步到DOM中后,执行该方法,可以在这个方法中做DOM操作。 除了首次render之后调用componentDidMount,其它render结束之后都是调用componentDidUpdate。

componentWillMount、componentDidMount和componentWillUpdate、componentDidUpdate可以对应起来。区别在于,前者只有在挂载的时候会被调用;而后者在以后的每次更新渲染之后都会被调用。 ps:绝对不要在componentWillUpdate和componentDidUpdate中调用this.setState方法,否则将导致无限循环调用。

  • 销毁阶段 该阶段主要发生组件销亡的时候,触发componentWillUnmount。当组件需要从DOM中移除的时候,通常需要做一些取消事件绑定,移除虚拟DOM中对应的组件数据结构,销毁一些无效的定时器等工作,都可以在这个方法中处理。

componentWillUnmount()

当组件要被从界面上移除的时候,就会调用 componentWillUnmount。 在这个函数中,可以做一些组件相关的清理工作,例如取消计时器、网络请求等。

一张图看懂组件的生命周期

生命周期调用和setState

生命周期 调用次数 能否使用setSate()
defaultProps / getDefaultProps 1(全局调用一次)
constructor / getInitialState 1
componentWillMount 1
render >=1
componentDidMount 1
componentWillReceiveProps >=0
shouldComponentUpdate >=0
componentWillUpdate >=0
componentDidUpdate >=0
componentWillUnmount 1

动手撸个demo加强下记忆


'use strict';

import React, { Component } from 'react';

import {
    StyleSheet,
    Text,
    View,
    Image
} from 'react-native';

import {
    Page,
    Button,
    ViewPort
} from '@ctrip/crn';


class Greeting extends Component {
    constructor(props){
        super(props)
        this.state = {
            name: props.name|| "Greeting",
            other:'other'
        }
        console.log('Greeting constructor');
    }
    refreshState(name){
        this.setState({
            name:name
        })
    }

    componentWillMount(){
        console.log('componentWillMount');
    }
    componentDidMount(){
        console.log("componentDidMount");
    }

    componentWillReceiveProps(nextProps){
        console.log("componentWillReceiveProps nextProps = ",nextProps);
        this.setState({
            name:nextProps.name
        })
    }
    shouldComponentUpdate(nextProps,nextState){
        console.log("shouldComponentUpdate nextProps = ",nextProps, " nextState = ",nextState);
        if(nextProps.name === "RN"){
            console.log("shouldComponentUpdate return false");
            return false;
        }
        return true;
    }
    componentWillUpdate(nextProps,nextState){
        console.log("componentWillUpdate nextProps = ",nextProps, " nextState = ",nextState);

    }
    componentDidUpdate(){
        console.log("componentDidUpdate");
    }

  render() {
        console.log("Greeting render");
    return (

<
Text
>
Hello {this.state.name}!
<
/Text
>

    );
  }
}


export default class Page1 extends Page {
    constructor(props){
        super(props)
        this.state = {
            index:0,
            pics:[{uri:"http://m.vstou.com/img/201510/dm1_5.jpg"},{uri:"http://m.vstou.com/img/201510/dm2_5.jpg"}],
            names:['CRN','RN']
        }
    }
    render() {
        console.log("super render");
        const { pics , names , index } = this.state;
        let pic = pics[index];
        let name = names[index]
        return (

<
ViewPort
>
<
View style={styles.container}
>
<
Greeting ref="greet" name={name}/
>
<
Button onPress={this._onPress.bind(this)} textStyle={{color:'black'}}
>
父组件setState
<
/Button
>
<
Button onPress={this._greetOnPress.bind(this)} textStyle={{color:'black'}}
>
Greeting setState
<
/Button
>
<
/View
>
<
/ViewPort
>

        );
    }

    _onPress(){
        const { index } = this.state
        let next = index ? 0 : 1
        console.log(" ================ 父组件setState ================");
        this.setState({
            index:next
        })
    }
    //子组件改变state
    _greetOnPress(){
        console.log(" ================ Greeting setState ================");
        this.refs.greet.refreshState("_greetOnPress")
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
    image:{
        width:200,
        height:200
    }
});

解析下:

  • 当页面首次渲染 :
super render -
>

Greeting constructor -
>

componentWillMount -
>

Greeting render -
>

componentDidMount
  • 点击 父组件setState props.name='RN':
super render -
>

componentWillReceiveProps nextProps =  Object {name: "RN"} -
>

shouldComponentUpdate nextProps =  Object {name: "RN"}  nextState =  Object {name: "RN", other: "other"}
shouldComponentUpdate return false
因为shouldComponentUpdate返回false,所以后续的更新不在执行
  • 点击 父组件setState props.name='CRN':
super render -
>

componentWillReceiveProps nextProps =  Object {name: "CRN"} -
>

shouldComponentUpdate nextProps =  Object {name: "CRN"}  nextState =  Object {name: "CRN", other: "other"} -
>

componentWillUpdate nextProps =  Object {name: "CRN"}  nextState =  Object {name: "CRN", other: "other"} -
>

Greeting render -
>

componentDidUpdate
  1. Greeting setState :
shouldComponentUpdate nextProps =  Object {name: "CRN"}  nextState =  Object {name: "_greetOnPress", other: "other"} -
>

componentWillUpdate nextProps =  Object {name: "CRN"}  nextState =  Object {name: "_greetOnPress", other: "other"} -
>

Greeting render -
>
 componentDidUpdate
因为是greet组件刷新的自己的状态,所以直接进入shouldComponentUpdate判断

state的工作原理和React.js完全一致,所以对于处理state的一些更深入的细节,你可以参阅React.Component API

接下来就要进入第二阶段了,深度了解CRNProps和State比较

props

props是properties的缩写,您可以把任意类型的数据传递给子组件,如此子组件的默认数据源就是props。

你可以在挂在组件的时候设置它的props:

let data = {name : xxx};

<
Component data = {data} /
>

在使用中可以通过this.props.data调用props属性,但是切记能用this.props.xxx修改属性,这会导致props错乱。如:

this.props.data = {name:'nextxxxx'}
// 这是错误的示范,切记不要修改props及其子属性,可以把props当做const类型来看待。
  • 设置默认值
static defaultProps = {
    name:'CRN'
};
  • 属性检查
static propTypes = {
    name:React.PropTypes.string
};

state

它是组件的内部状态属性,主要用来存储组件自身需要的数据。 除了初始化时可能由props来决定,之后就完全由组件自身去维护。 组件中由系统定义了setState方法,每次调用setState时都会更新组件的状态,触发render方法重新渲染界面。 需要注意的是render方法是被异步调用的,这可以保证同步的多个setState方法只会触发一次render,这样做是有利于提高性能的。

  • 设置默认值
    constructor(props){
        super(props)
        this.state = {
            showName:'CRN'
        }
        //切记,在此处不要使用setState()
    }
  • 刷新页面状态
componmentWillMount(){
    this.setState({
        showName:'RN'
    })
}

提到state就不得不提组件的生命周期

组件生命周期

  • 创建阶段

该阶段主要发生在创建组件类的时候,在这个阶段中会初始化组件的属性类型和默认属性

static defaultProps = {
        name: ..,
};  // 注意这里有分号
static propTypes = {
        name: React.PropTypes.string.isRequired,
};  // 注意这里有分号
  • 实例化阶段

该阶段主要发生在组件类被调用(实例化)的时候。

1、constructor 这里是对控件的一些状态进行初始化,由于该函数不同于defaultProps,在以后的过程中,会再次调用,所以可以将控制控件的状态的一些变量放在这里初始化.

    constructor(props){
        super(props)
        this.state = {
            showName:'CRN'
        }
        //切记,在此处不要使用setState()
    }

2、componentWillMount()

准备加载组件。 这个调用时机是在组件创建,并初始化了状态之后,在第一次绘制 render() 之前。可以在这里做一些业务初始化操作,也可以设置组件状态。这个函数在整个生命周期中只被调用一次。 如果在这个函数里面调用setState,本次的render函数可以看到更新后的state,并且只渲染一次。

componmentWillMount(){
    this.setState({
        showName:'RN'
    })
}

3、render()

render是一个组件必须有的方法,形式为一个函数,渲染界面,并返回JSX或其他组件来构成DOM,和Android的XML布局、WPF的XAML布局类似,只能返回一个顶级元素。

4、componentDidMount()

调用了render方法后,组件加载成功并被成功渲染出来以后所执行的hook函数,一般会将网络请求等加载数据的操作,放在这个函数里进行,来保证不会出现UI上的错误。

  • 运行(更新)阶段

1、componentWillReceiveProps(nextProps)

当组件接收到新的props时,会触发该函数。在该函数中,通常可以调用setState()来完成对state的修改。 输入参数 nextProps 是即将被设置的属性,旧的属性还是可以通过 this.props 来获取。在这个回调函数里面,你可以根据属性的变化,通过调用 this.setState() 来更新你的组件状态,这里调用更新状态是安全的,并不会触发额外的 render() 调用

componentWillReceiveProps: function(nextProps) {  
  this.setState({
    showName: nextProps.ShowName
  });
}

2、shouldComponentUpdate(nextProps, nextState)

返回布尔值(决定是否需要更新组件)

输入参数 nextProps 和上面的 componentWillReceiveProps 函数一样,nextState 表示组件即将更新的状态值。这个函数的返回值决定是否需要更新组件,如果 true 表示需要更新,继续走后面的更新流程。否者,则不更新,直接进入等待状态。 默认情况下,这个函数永远返回 true 用来保证数据变化的时候 UI 能够同步更新。在大型项目中,你可以自己重载这个函数,通过检查变化前后属性和状态,来决定 UI 是否需要更新,能有效提高应用性能。

3、componentWillUpdate(nextProps, nextState)

shouldComponentUpdate返回true或者调用forceUpdate之后,就会开始准更新组件,并调用 componentWillUpdate()。 输入参数与 shouldComponentUpdate 一样,在这个回调中,可以做一些在更新界面之前要做的事情。需要特别注意的是,在这个函数里面,你就不能使用 this.setState 来修改状态。这个函数调用之后,就会把 nextProps 和 nextState 分别设置到 this.props 和 this.state 中。紧接着这个函数,就会调用 render() 来更新界面了。

4、render()

再确定需要更新组件时,调用render,根据diff算法,渲染界面,生成需要更新的虚拟DOM数据。

5、componentDidUpdate()

虚拟DOM同步到DOM中后,执行该方法,可以在这个方法中做DOM操作。 除了首次render之后调用componentDidMount,其它render结束之后都是调用componentDidUpdate。

componentWillMount、componentDidMount和componentWillUpdate、componentDidUpdate可以对应起来。区别在于,前者只有在挂载的时候会被调用;而后者在以后的每次更新渲染之后都会被调用。 ps:绝对不要在componentWillUpdate和componentDidUpdate中调用this.setState方法,否则将导致无限循环调用。

  • 销毁阶段 该阶段主要发生组件销亡的时候,触发componentWillUnmount。当组件需要从DOM中移除的时候,通常需要做一些取消事件绑定,移除虚拟DOM中对应的组件数据结构,销毁一些无效的定时器等工作,都可以在这个方法中处理。

componentWillUnmount()

当组件要被从界面上移除的时候,就会调用 componentWillUnmount。 在这个函数中,可以做一些组件相关的清理工作,例如取消计时器、网络请求等。

一张图看懂组件的生命周期

生命周期调用和setState

生命周期 调用次数 能否使用setSate()
defaultProps / getDefaultProps 1(全局调用一次)
constructor / getInitialState 1
componentWillMount 1
render >=1
componentDidMount 1
componentWillReceiveProps >=0
shouldComponentUpdate >=0
componentWillUpdate >=0
componentDidUpdate >=0
componentWillUnmount 1

动手撸个demo加强下记忆


'use strict';

import React, { Component } from 'react';

import {
    StyleSheet,
    Text,
    View,
    Image
} from 'react-native';

import {
    Page,
    Button,
    ViewPort
} from '@ctrip/crn';


class Greeting extends Component {
    constructor(props){
        super(props)
        this.state = {
            name: props.name|| "Greeting",
            other:'other'
        }
        console.log('Greeting constructor');
    }
    refreshState(name){
        this.setState({
            name:name
        })
    }

    componentWillMount(){
        console.log('componentWillMount');
    }
    componentDidMount(){
        console.log("componentDidMount");
    }

    componentWillReceiveProps(nextProps){
        console.log("componentWillReceiveProps nextProps = ",nextProps);
        this.setState({
            name:nextProps.name
        })
    }
    shouldComponentUpdate(nextProps,nextState){
        console.log("shouldComponentUpdate nextProps = ",nextProps, " nextState = ",nextState);
        if(nextProps.name === "RN"){
            console.log("shouldComponentUpdate return false");
            return false;
        }
        return true;
    }
    componentWillUpdate(nextProps,nextState){
        console.log("componentWillUpdate nextProps = ",nextProps, " nextState = ",nextState);

    }
    componentDidUpdate(){
        console.log("componentDidUpdate");
    }

  render() {
        console.log("Greeting render");
    return (

<
Text
>
Hello {this.state.name}!
<
/Text
>

    );
  }
}


export default class Page1 extends Page {
    constructor(props){
        super(props)
        this.state = {
            index:0,
            pics:[{uri:"http://m.vstou.com/img/201510/dm1_5.jpg"},{uri:"http://m.vstou.com/img/201510/dm2_5.jpg"}],
            names:['CRN','RN']
        }
    }
    render() {
        console.log("super render");
        const { pics , names , index } = this.state;
        let pic = pics[index];
        let name = names[index]
        return (

<
ViewPort
>
<
View style={styles.container}
>
<
Greeting ref="greet" name={name}/
>
<
Button onPress={this._onPress.bind(this)} textStyle={{color:'black'}}
>
父组件setState
<
/Button
>
<
Button onPress={this._greetOnPress.bind(this)} textStyle={{color:'black'}}
>
Greeting setState
<
/Button
>
<
/View
>
<
/ViewPort
>

        );
    }

    _onPress(){
        const { index } = this.state
        let next = index ? 0 : 1
        console.log(" ================ 父组件setState ================");
        this.setState({
            index:next
        })
    }
    //子组件改变state
    _greetOnPress(){
        console.log(" ================ Greeting setState ================");
        this.refs.greet.refreshState("_greetOnPress")
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
    image:{
        width:200,
        height:200
    }
});

解析下:

  • 当页面首次渲染 :
super render -
>

Greeting constructor -
>

componentWillMount -
>

Greeting render -
>

componentDidMount
  • 点击 父组件setState props.name='RN':
super render -
>

componentWillReceiveProps nextProps =  Object {name: "RN"} -
>

shouldComponentUpdate nextProps =  Object {name: "RN"}  nextState =  Object {name: "RN", other: "other"}
shouldComponentUpdate return false
因为shouldComponentUpdate返回false,所以后续的更新不在执行
  • 点击 父组件setState props.name='CRN':
super render -
>

componentWillReceiveProps nextProps =  Object {name: "CRN"} -
>

shouldComponentUpdate nextProps =  Object {name: "CRN"}  nextState =  Object {name: "CRN", other: "other"} -
>

componentWillUpdate nextProps =  Object {name: "CRN"}  nextState =  Object {name: "CRN", other: "other"} -
>

Greeting render -
>

componentDidUpdate
  1. Greeting setState :
shouldComponentUpdate nextProps =  Object {name: "CRN"}  nextState =  Object {name: "_greetOnPress", other: "other"} -
>

componentWillUpdate nextProps =  Object {name: "CRN"}  nextState =  Object {name: "_greetOnPress", other: "other"} -
>

Greeting render -
>
 componentDidUpdate
因为是greet组件刷新的自己的状态,所以直接进入shouldComponentUpdate判断

state的工作原理和React.js完全一致,所以对于处理state的一些更深入的细节,你可以参阅React.Component API

接下来就要进入第二阶段了,深度了解CRN

results matching ""

    No results matching ""