React 生命周期的理解

React 生命周期的理解

在组件的整个生命周期中,随着该组件的 props 或者 state 发生改变,其 DOM 表现也会有相应的变化。一个组件就是一个状态机,对于特定地输入,它总返回一致的输出。
一个 React 组件生命周期三个部分实例化、存在期和销毁时。

实例化

getDefaultProps

  • 对于每个组件实例来讲,这个方法只会调用一次,该组件类的所有后续应用,getDefaultPops 将不会再被调用,其返回的对象可以用于设置默认的值。
  • ES6 以后可以使用component.defultProps={xx:xx}来设置 props 默认值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import React, { Component } from "react";

// ES6的写法
export default class Hello extends Component {
render() {
return (
<div>
Hello,{this.props.name},my username is {this.props.dwqs}
</div>
);
}
}

Hello.defultProps = {
name: "pomy",
dwqs: "dwqs",
};

// ES5的写法
var Hello = React.creatClass({
getDefaultProps: function () {
return {
name: "pomy",
dwqs: "dwqs",
};
},

render: function () {
return (
<div>
Hello,{this.props.name},my username is {this.props.dwqs}
</div>
);
},
});

getInitialState

  • 对于组件的每个实例来说,这个方法的调用有且只有一次,用来初始化每个实例的 state,在这个方法里,可以访问组件的 props。
  • ES6 初始化 state,一般都会放在构造函数中
  • 每一个 React 组件都有自己的 state,其与 props 的区别在于 state 只存在组件的内部,props 在所有实例中共享
  • getInitialStategetDefaultPops 的调用是有区别的,getDefaultPops 是对于组件类来说只调用一次,后续该类的应用都不会被调用,而 getInitialState 是对于每个组件实例来讲都会调用,并且只调一次。
  • 每次修改 state,都会重新渲染组件,实例化后通过 state 更新组件,会依次调用下列方法:
    1. shouldComponentUpdate
    2. componentWillUpdate
    3. render
    4. componentDidUpdate
  • 不要直接修改 this.state,要通过 this.setState 方法来修改。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  import React, { Component } from 'react'

// ES6的写法
export default class LikeButton extends Component {
constructor(props) {
super(props)
this.state = {
liked: false
}
}

handleClick = (event) =>{
this.setState({
liked: !this.state.liked
});
},

render() {
let text = this.state.liked ? 'like' : 'haven\'t liked';
return (
<p onClick={this.handleClick}>
You {text} this. Click to toggle.
</p>
);
}
}

// ES5的写法
var LikeButton = React.createClass({
getInitialState: function() {
return {liked: false};
},
handleClick: function(event) {
this.setState({liked: !this.state.liked});
},
render: function() {
var text = this.state.liked ? 'like' : 'haven\'t liked';
return (
<p onClick={this.handleClick}>
You {text} this. Click to toggle.
</p>
);
}
});


componentWillMount (yes)

  • 该方法在首次渲染之前调用,也是再 render 方法调用之前修改 state 的最后一次机会
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React, { Component } from "react";
// ES6的写法
export default class Hello extends Component {
//渲染之前调用
componentWillMount() {
// 此时可以setState
this.setState({
xx: xx,
});
}

render() {
return (
<div>
Hello,{this.props.name},my username is {this.props.dwqs}
</div>
);
}
}

Hello.defultProps = {
name: "pomy",
dwqs: "dwqs",
};

render

  • 该方法会创建一个虚拟 DOM,用来表示组件的输出
  • 对于一个组件来讲,render 方法是唯一一个必需的方法
  • render 方法需要满足下面几点:
    1. 只能通过 this.props 和 this.state 访问数据(不能修改)
    2. 可以返回 null,false 或者任何 React 组件
    3. 只能出现一个顶级组件,不能返回一组元素
    4. 不能改变组件的状态
    5. 不能修改 DOM 的输出
  • render 方法返回的结果并不是真正的 DOM 元素,而是一个虚拟的表现,类似于一个 DOM tree 的结构的对象。react 之所以效率高,就是这个原因

componentDidMount (yes)

  • 该方法不会在服务端被渲染的过程中调用该方法被调用时,已经渲染出真实的 DOM.
  • 可以再该方法中通过 this.getDOMNode() 访问到真实的 DOM(推荐使用 ReactDOM.findDOMNode())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React, { Component } from "react";
// ES6的写法
export default class Hello extends Component {
//render之后调用,此时可访问DOM节点
componentDidMount() {
// 此时可以setState
this.setState({
xx: xx,
});
}

render() {
return (
<div>
Hello,{this.props.name},my username is {this.props.dwqs}
</div>
);
}
}

Hello.defultProps = {
name: "pomy",
dwqs: "dwqs",
};
  • 由于组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React, { Component } from 'react'
// ES6的写法
export default class Hello extends Component {
constructor(props) {
super(props);
this.textInput = null;
}

this.setTextInputRef = element => {
this.textInput = element;
};

this.focusTextInput = () => {
// Focus the text input using the raw DOM API
if (this.textInput) this.textInput.focus();
};

//render之后调用,此时可访问DOM节点
componentDidMount(){
this.focusTextInput();
//这是有效的,可以访问到 Canvas 节点
}

render() {
return (
<input
type="text"
ref={this.setTextInputRef}
/>
)
}
}


存在期

componentWillReceiveProps (yes)

  • 组件的 props 属性可以通过父组件来更改,这时,componentWillReceiveProps 将来被调用。
  • 可以在这个方法里更新 state,以触发 render 方法重新渲染组件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React, { Component } from "react";
// ES6的写法
export default class Hello extends Component {
//当接收到新的props时调用
componentWillReceiveProps(nextProps) {
// 此时可以setState
if (nextProps.xx !== undefined) {
this.setState({
xx: nextProps.xx,
});
}
}

render() {
return <div>hello</div>;
}
}

shouldComponentUpdate

  • 如果你确定组件的 props 或者 state 的改变不需要重新渲染,可以通过在这个方法里通过返回 false 来阻止组件的重新渲染,返回 false 则不会执行 render 以及后面的 componentWillUpdatecomponentDidUpdate 方法
  • 该方法是非必须的,并且大多数情况下没有在开发中使用
1
2
3
4
5
6
7
8
9
10
11
12
13
import React, { Component } from "react";
// ES6的写法
export default class Hello extends Component {
//获得新的props或state后,返回true继续渲染,返回false则不会render渲染
shouldComponentUpdate(nextProps, nextState) {
return this.state.checked === nextState.checked;
//return false 则不更新组件
}

render() {
return <div>hello</div>;
}
}

componentWillUpdate

  • 这个方法和 componentWillMount 类似,在组件接收到了新的 props 或者 state 即将进行重新渲染前
  • componentWillUpdate(object nextProps, object nextState) 会被调用,注意不要在此方面里再去更新 props 或者 state

componentDidUpdate

  • 这个方法和 componentDidMount 类似,在组件重新被渲染之后,componentDidUpdate(object prevProps, object prevState)会被调用
  • 可以在这里访问并修改 DOM

销毁时

componentWillUnmount

  • 每当 React 使用完一个组件,这个组件必须从 DOM 中卸载后被销毁,此时 componentWillUnmout 会被执行,完成所有的清理和销毁工作,
  • componentDidMount 中添加的任务都需要再该方法中撤销,如创建的定时器或事件监听器。

react 生命周期图

image.jpg