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了。
然而这是一个反模式,最好不要这么写。