Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

为什么我的this.props.children不能re-render? #4

Open
hufeng opened this issue Feb 20, 2016 · 1 comment
Open

为什么我的this.props.children不能re-render? #4

hufeng opened this issue Feb 20, 2016 · 1 comment

Comments

@hufeng
Copy link
Owner

hufeng commented Feb 20, 2016

困于心的原因常常没有对事物本质有个清晰的认知,导致认识上的偏差

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。

原来就是这样!

@gxthrj
Copy link

gxthrj commented Feb 23, 2016

大师威武~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants