1. 生命周期
React父子组件体系中,首次渲染的生命周期函数,触发情况如下,
Father: componentWillMount
Father: render
Son: componentWillMount
Son: render
Son: componentDidMount
Father: componentDidMount
等渲染完后,在父组件的componentDidMount
中调用setState
,
生命周期函数,触发情况如下,
Father: setState
Father: shouldComponentUpdate ---- true
Father: componentWillUpdate
Father: render
Son: componentWillReceiveProps
Son: shouldComponentUpdate ---- true
Son: componentWillUpdate
Son: render
Son: componentDidUpdate
Father: componentDidUpdate
注:
父组件或子组件的shouldComponentUpdate
函数是关键,如果返回false
,
会导致其后的componentWillUpdate
,render
和componentDidUpdate
都不执行。
2. componentWillReceiveProps的重要性
在实际开发过程中,如果父组件改变了子组件的属性值,
并且,子组件是通过自身state
来渲染页面的话,
就必须在子组件的componentWillReceiveProps
中调用,setState(nextProps)
,
componentWillReceiveProps(nextProps){
this.setState(nextProps);
}
否则,因为子组件的state
没有任何改变,
虽然会经历shouldComponentUpdate
,componentWillUpdate
,render
和componentDidUpdate
,
但是因为Diff算法的原因,render
函数也不会更新DOM。
3. 子组件不更新
下面我们来看一个真实场景的例子。
我们有Father
和Son
两个组件,
(1)父组件改变了子组件的属性值
由于Father
组件通过在自身componentDidMount
中调用setState
,改变了自身的state
,
所以,Father
组件的生命周期函数shouldComponentUpdate
,componentWillUpdate
,render
依次被调用,
在render
中,y={this.state.x}
改变了子组件的属性值。
class Father extends Component {
constructor(props) {
super(props);
this.state = {
x: 1
};
}
componentDidMount() {
setTimeout(() => this.setState({
x: 2
}), 1000);
}
render() {
return (
<Son y={this.state.x} />
);
}
}
(2)子组件是通过自身state
来渲染页面
在Son
子组件中,使用了自身state
来渲染页面,{this.state.y}
。
那么,如果子组件componentWillReceiveProps
不进行setState
,
Father
组件对Son
组件属性的影响就不会改变Son
组件的state
。
虽然Son
组件的shouldComponentUpdate
,componentWillUpdate
,render
都会被调用,
但是由于state
没有更新,DOM也没有更新。
class Son extends Component {
constructor(props) {
super(props);
this.state = {
y: props.y
};
}
// 反模式
// componentWillReceiveProps(nextProps){
// this.setState(nextProps);
// }
render() {
return (
<div>{this.state.y}</div>
);
}
}
去掉以上componentWillReceiveProps
的注释,
通过setState
,子组件就更新了。
此外,如果Son
组件中,直接使用{this.props.y}
来渲染,也可以避免这个问题。
因为,这样会改变div
组件的属性,导致div
组件更新。
4. 诡异的反模式
我在实际项目中,遇到了一个子组件在被更新属性时不setState
的例子,
但是却惊奇的发现它居然可以更新DOM。
原因是,父组件的render
函数,写法很诡异,
每次render
都创建一个新的组件,例如,
render() {
const A = () => (
<Son y={this.state.x} />
);
return (
<A />
);
}
这样实际每次render
的是新组件<A />
,
同时Son
组件也会被新建,(可验证componentWillMount
又触发了)
肯定更新DOM了。
然而这是一个反模式,最好不要这么写。
网友评论