生命周期
# 生命周期
生命周期是 React 组件从装载至卸载的全过程,这个过程中有多个钩子函数。
# constructor 构造器
在组件挂载前会先调用构造函数,在为 React.Component 的子类实现构造函数时,应当在所有语句之前调用 super(props)
。构造器通常用于初始化 state 或者为事件处理函数绑定 this。
Note
构造器中初始化 state 时应该直接赋值而不是使用 setState,其他地方要修改 state 应该调用 setState。
# static getDerivedStateFromProps(props, state)
此方法会在调用 render 方法前调用,并且在初始挂载及后续更新时都会调用,这个方法应当返回一个对象来更新 state,如果返回 null 则不更新任何内容。
class Foo extends React.Component {
static getDerivedStateFromProps(props, state) {
return null
}
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
Foo
</div>
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# render
此方法是类组件中唯一必须要实现的方法,此函数应该是一个纯函数。
# componentDidMount()
此方法会在组件挂载后立即调用,此方法可以用来发起远程请求初始化数据等。
const mockReq = () => {
return new Promise((res) => {
setTimeout(() => {
res({value: '123'});
}, 100);
})
}
class Foo extends React.Component {
state = {
value: ''
}
render() {
return (
<div>
<span>value: </span>
<span>{this.state.value}</span>
</div>
);
}
async componentDidMount() {
console.log('calling')
const {value} = await mockReq();
console.log(value)
this.setState({value})
}
}
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
# shouldComponentUpdate(nextProps, nextState)
当组件因为 state 和 props 变化而发生更新时,在重新渲染前该函数会被触发。
此函数如果返回 true 那么将调用 render 继续进行组件的渲染,否则停止渲染,render 及后续其他生命周期函数将不会再被触发。
Warning
此函数中不应该再调用 setState,否则会因为重新触发渲染而导致死循环。
下面是一个例子,点击按钮递增,只有是传入 props 的倍数才重新渲染:
class Foo extends React.Component {
state = {
value: 0
}
render() {
return (
<div>
<span>{this.state.value}</span>
<button onClick={() => {
this.setState({
value: this.state.value+1,
})
}}>click</button>
</div>
);
}
shouldComponentUpdate(nextProps, nextState) {
const { step } = nextProps;
const { value } = nextState;
return value % step === 0
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# getSnapshotBeforeUpdate(prevProps, prevState)
此方法在最近一次渲染输出前调用,即提交到 DOM 节点之前,这可以让组件在发生更改前从 DOM 中捕获一些信息。
此方法的任何返回值将作为参数传递给 componentDidUpdate()
。
例如,如果希望在更新后网页滚动的位置保持不变,可以在此方法中获取之前滚动的位置。
class Foo extends React.Component {
state = {
arr: []
}
ulRef = React.createRef();
componentDidMount() {
setInterval(() => {
this.setState({arr: [`item${this.state.arr.length}`, ...this.state.arr]});
}, 1000)
}
getSnapshotBeforeUpdate(prevProps, prevState) {
return this.ulRef.current.scrollHeight;
}
componentDidUpdate(props, state, prevHeight) {
this.ulRef.current.scrollTop += this.ulRef.current.scrollHeight - prevHeight;
}
render() {
return (
<div>
<ul style={{height: '200px', width: '400px', overflowY: 'auto', border: '1px solid #ccc'}} ref={this.ulRef}>
{
this.state.arr.map((item, index) => {
return <li key={index} style={{height: '50px'}}>{item}</li>
})
}
</ul>
</div>
);
}
}
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
scrollHeight 是只读属性,表示元素内容的整体高度,包括由于溢出导致在视线外的部分;scrollTop 是元素中被隐藏在滚动视图上方的元素高度。在 getSnapshotBeforeUpdate 中获取到的 scrollHeight 表示重新渲染之前的 ul 的高度,并将这个值返回给 componentDidUpdate 方法,此方法中再次获取到的 scrollHeight 的值为重新渲染之后的值,两者的差值就是新插入元素的高度,将 scrollTop 不断累加这个差值即可实现滚动条保持不变。
# componentDidUpdate(nextProps, nextState, snapshot)
此方法将会在每次重新渲染后触发。
componentDidUpdate(nextProps, nextState, snapshot) {
console.log(snapshot)
}
getSnapshotBeforeUpdate(prevProps, prevState) {
return {
prevProps,
prevState
}
}
2
3
4
5
6
7
8
9
# componentWillUnmount()
此方法会在组件卸载及销毁之前直接调用,此方法中不应该再调用 setState()
因为此组件将永远不会再渲染。
class Foo extends React.Component {
render() {
return (
<div>
<h1>title</h1>
<button onClick={() => {
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}}>remove</button>
</div>
);
}
componentWillUnmount() {
console.log('GG')
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15