iThink
iThink copied to clipboard
为什么我的this.props.children不能re-render?
困于心的原因常常没有对事物本质有个清晰的认知,导致认识上的偏差
Container Component VS Pure Component
React与很多view层框架一个很不同的地方就是强调数据的单项数据流动。这点也是iflux在设计的时候特别强调的,针对这种单向的数据流动,React的组件就变成两种 Container Component 和 Pure Component。
Why Container Component
理论上在React组件中,Pure Component越多越好,带来更好的组件重用。Container Component主要可以帮助我们更好的分离关注点,例如:
render() {
if (this.props.isLoading) {
return <QMLoaing/>
} else {
return <QMUserProfile/>
}
}
在app中可能很多的地方都在使用QMLoading来完成页面的loading效果,每个组件中这样写就很不优雅,大量的重复代码,而且整个组件的组合都不够声明式。
这样时候我们就可以很好的使用Container Component去重构我们的应用,首先抽象一个LoadingContainer.
class LoadingContainer extends React.Component {
render() {
if (this.props.isLoading) {
return <QMLoading/>
} else {
return <View>{ this.props.children }</View>
}
}
}
how to use?
import LoadingContainer from 'LoadingContainer';
class User extends React.Component {
render() {
return (
<LoadingContainer isLoading={ true }>
<UserProfile/>
</LoadingContainer>
);
}
}
是不是优雅很多?:)
但是这个里面有一个很容易忽略的一个问题,这里面的UserProfile的渲染并不受LoadingContainer来控制,而是User。比如如果LoadingContainer里面状态的更新引起re-render,但是这个re-render并不会引起
<UserProfile/>
的re-render,只有User去re-render才会去引起UserProfile的更新。
例如:
class HelloContainer extends React.Component {
componentDidMount() {
setTimeout(() => {
this.forceUpdate();
}, 2000);
}
render() {
return <div> { this.props.children } </div>;
}
}
class Hello extends React.Component {
render() {
return <div>{ this.props.random } </div>;
}
}
class App extends React.Component {
render() {
return (
<HelloContainer>
<Hello name={Math.random()}/>
</HelloContainer>
);
}
}
这里面就是当HelloContainer去forceUpdate的时候,并不引起this.props.children去更新。
这是为什么呢? 直觉的方式去HelloContainer中的render方法console.log下this.props.children到底是什么?
结果却是打印出一个对象,是一个对象,为什么是一个对象?到底this.props.cchildren代表什么,我们做一些小的变动。
class HelloContainer extends React.Component {
componentDidMount() {
setTimeout(() => {
this.forceUpdate();
}, 2000);
}
render() {
return <div> { this.props.children() } </div>;
}
}
class Hello extends React.Component {
render() {
return <div>{ this.props.random } </div>;
}
}
class App extends React.Component {
render() {
return (
<HelloContainer>
{ () => <Hello name={Math.random()}/> }
</HelloContainer>
);
}
}
这回正如我们所料,当HelloContainer更新的时候,Hello组件也更新了。
这次我们看的比较清楚了,this.props.children就是
{ () => <Hello name={Math.random()}/> }
那上面的例子,this.props.children是什么?是
<Hello name={Math.random()}/>
Oh, NO! 当然不是,如果是打印出来应该是一个函数。也许你已经猜到了是这个组件对于的render后的对象。为什么?
这里面容易被JSX迷惑,Hello只是React.createElement优雅表象形式。
因为React在渲染的时候,先渲染子节点,在渲染父节点。这里就先渲染Hello, 渲染Hello本质上就是去调用了下React.createElement然后返回virtualdom对象,
这个对象就是HelloContainer中this.props.children对象, 所有当HelloContainer去forceUpdate时候这个对象不会去re-render。
原来就是这样!
大师威武~