React以递增方式呈现子项

时间:2018-01-12 14:38:08

标签: javascript reactjs

我们说我有一个React元素<Parent&gt;我需要渲染一个<FooBar>

的数组

我的代码目前看起来像

Parent = React.createClass({

    getChildren() {
        var children = this.props.messages; // this could be 100's
        return children.map((child) => {
        return <FooBar c={child} />;
      });
    },

    render() {
      return (
        <div>
          {this.getChildren()}
        </div>
      );
    }
 });

当有100个孩子因为Parent等待所有孩子渲染时,这真的很慢。是否有一种解决方法来逐步呈现子项,以便Parent不必等待其所有子项都呈现?

2 个答案:

答案 0 :(得分:1)

您可以一次获取一部分消息进行渲染,然后通过状态计数为更多子项排队进一步更新。

使用requestIdleCallbacksetTimeout进行排队将允许您逃避React的状态批处理以拦截当前的浏览器绘制,如果您直接setState这将是一个问题来自componentDidUpdate

有什么能让你前进

const Parent = React.createClass({

  numMessagesPerRender = 10

  constructor(props) {
    super(props)
    this.state = {
      renderedCount: 0
    }
  }

  componentWillReceiveProps(props) {
    // must check that if the messages change, and reset count if you do
    // you may also need to do a deep equality of values if you mutate the message elsewhere
    if (props.messages !== this.props.messages) {
      this.setState({renderedCount: 0})
    }
  }

  getChildren() {
      // take only the current
      const children = this.props.messages.slice(0, this.state.renderedCount);
      return children.map(child => <FooBar c={child} />);
  },

  render() {
    return (
      <div>
        {this.getChildren()}
      </div>
    );
  }

  renderMoreMessagesPlease() {
    // you MUST include an escape condition as we are calling from `componentDidXYZ`
    // if you dont your component will get stuck in a render loop and crash
    if (this.state.renderedCount < this.props.messages.length) {
      // queue up state change until the frame has been painted
      // otherwise setState can halt rendering to do batching of state changes into a single
      // if your browser doesnt support requestIdleCallback, setTimeout should do same trick
      this.idleCallbackId = requestIdleCallback(() => this.setState(prevState => ({
        renderedCount: prevState.renderedCount + this.numMessagesPerRender
      })))
    }
  } 

  componentDidMount() {
    this.renderMoreMessagesPlease()
  }

  componentDidUpdate() {
    this.renderMoreMessagesPlease()
  }

  componentDidUnmount() {
    // clean up so cant call setState on an unmounted component
    if (this.idleCallbackId) {
      window.cancelIdleCallback(this.idleCallbackId)
    }
  }

});

答案 1 :(得分:0)

你应该利用承诺和国家。这是一个简单的解决方案。

Parent = React.createClass({
    getInitialState() {
      return {
        children: []
      }
    },

    componentDidMount() {
      var prom = new Promise(function(resolve, reject){
        if (this.props.messages) {
          for(var i = 0;i < this.props.messages.length; i++) {
            const children = this.state.children;
            children.push(this.props.messages[i]);
            this.setState({children});
          }
          resolve();
        }
        else {
          reject();
        }
      })
      prom.then(()=>{
        console.log('All children added')
      }).catch(()=>{
        console.log('could not get props.messages')
      })
    },

    render() {
      return (
        <div>
          {this.state.children.map((child)=>(<FooBar c={child} />))}
        </div>
      );
    }
 });