React不呈现递归反应组件

时间:2016-08-13 02:08:23

标签: javascript reactjs ecmascript-6

鉴于此组件:

import React, { Component } from 'react';
import TrackerReact from 'meteor/ultimatejs:tracker-react';

export default class SubscriptionView extends TrackerReact(Component) {

  constructor(props) {
    super(props);

    let params = props.params || [];

    if (!Array.isArray(params)) {
      params = [params];
    }

    this.state = {
      subscription: {
        collection: Meteor.subscribe(props.subscription, ...params)
      }
    };
  }

  componentWillUnmount() {
    this.state.subscription.collection.stop();
  }


  render() {
    let loaded = this.state.subscription.collection.ready();

    if (!loaded) {
      return (
        <section className="subscription-view">
          <h3>Loading...</h3>
        </section>
      );
    }

    return (
      <section className="subscription-view">
        { this.props.children }
      </section>
    );
  }
};

另一个组成部分:

import SubscriptionView from './SubscriptionView.jsx';

export const Foo = () => (
  <SubscriptionView subscription="allFoo">
    <SubscriptionView subscription="singleBar" params={ 123 }>
      <div>Rendered!</div>
    </SubscriptionView>
  </SubscriptionView>
);

当数据可用时,第一个Subscription会被重新渲染,但第二个只渲染一次而不再渲染。如果我在console.log(this.props.subscription, ready);的渲染函数中放置SubscriptionView,我会看到

allFoo false
allFoo true
singleBar false

就是这样。

在服务器端,两种发布方法都是

Meteor.publish('allFoo', function () {
  console.log("Subscribing foos");
  return Foos.find();
});

Meteor.publish('singleBar', function (id) {
  console.log("Subscribing bar", id);
  return Bars.find({ _id: id });
});

正在调用这两种发布方法。

为什么不是第二个SubscriptionView被动?

*解决方案*

这是基于alexi2的评论:

import React, { Component } from 'react';
import TrackerReact from 'meteor/ultimatejs:tracker-react';

export default class SubscriptionLoader extends TrackerReact(Component) {

  constructor(props) {
    super(props);

    let params = props.params || [];

    if (!Array.isArray(params)) {
      params = [params];
    }

    this.state = {
      done: false,
      subscription: {
        collection: Meteor.subscribe(props.subscription, ...params)
      }
    };
  }

  componentWillUnmount() {
    this.state.subscription.collection.stop();
  }

  componentDidUpdate() {
    if (!this.state.done) {
      this.setState({ done: true });

      this.props.onReady && this.props.onReady();
    }
  }


  render() {
    let loaded = this.state.subscription.collection.ready();

    if (!loaded) {
      return (
        <div>Loading...</div>
      );
    }

    return null;
  }
};

然后,在父组件的render方法中:

<section className="inventory-item-view">
  <SubscriptionLoader subscription='singleBar' params={ this.props.id } onReady={ this.setReady.bind(this, 'barReady') } />
  <SubscriptionLoader subscription='allFoos' onReady={ this.setReady.bind(this, 'foosReady') } />
  { content }
</section>

setReady仅设置组件的状态,content仅在this.state.barReady && this.state.foosReady为真时才有值。

有效!

1 个答案:

答案 0 :(得分:2)

尝试将SubscriptionView组件分离出来:

import SubscriptionView from './SubscriptionView.jsx';

export const Foo = () => (
    <div>
      <SubscriptionView subscription="singleBar" params={ 123 }>
        <div>Rendered!</div>
      </SubscriptionView>
      <SubscriptionView subscription="allFoo">
        <div>Rendered Again!</div>
      </SubscriptionView>
    </div>
);

根据评论对话进行修改

不确定我是否在正确的轨道上但您可以将Foo构建为“智能”组件,根据需要将props传递给每个SubscriptionView,然后使用Foo作为可重用组件。

  

让我们说我需要呈现的是FooBarForm,它需要在特定用例中注册Foos和Bars。你会怎么做?

您可以创建根据需要获取道具的组件FoosBars,并创建包含这些组件并传递必要数据的父组件FooBarForm

FooBarForm将处理状态,并且当更改时将其传递给其子组件的props。

现在,状态由父组件集中管理,子组件使用从父组件传递的props进行渲染。

子组件会在其道具发生变化时重新渲染,具体取决于从父组件传递的状态是否已更改。