当道具改变时,反应流星数据容器不会更新孩子

时间:2017-07-16 10:35:32

标签: reactjs meteor

我一直在努力解决这个问题很长一段时间,但未找到任何答案。

我使用 react-meteor-data 来管理我的数据,并在我的流星应用程序中做出反应。在处理mongo的数据时工作正常,但我不能让它与道具反应。

App.js 中,我调用了我想要反应的容器,并在App状态发生变化时重新呈现。

<MyContainer someState={this.state.MyState} />

MyContainer.js 中,我有一个来自 react-meteor-data 的<{1}}

createContainer

第一次渲染组件时,此工作正常,export default createContainer(params => { Meteor.subscribe('someCollection'); return { someCollection: SomeCollection.find({}).fetch(), stateFromParent: params.someState }; }, MyContainer); 正确获取MyContainer

问题是,当MyStateMyState发生变化时,我可以在Chrome Dev React工具中看到它确实已更新为AppcreateContainer有一个具有正确更新状态的prop)但ReactMeteorDataComponent函数未运行,因此createContainer不会更新props

因此,MyContainerprops更新,但不会更新ReactMeteorDataComponent无限期保留数据。它就像MyContainer并不认为其道具的更新有变化,因此不会运行其功能。

我真的觉得我错过了一些东西,因为这似乎是非常基本的东西,谢谢你的帮助。

1 个答案:

答案 0 :(得分:0)

OP没有提到国家是如何改变的,所以最初的例子是不完整的。因此,我将尝试解释容器创建如何工作的要点,希望理解它是有用的。

它是如何工作的?

它使用meteor的Tracker在计算失效时自动更新包装组件(即,当其中一个反应数据源(如反应变量,订阅句柄或获取的MiniMongo游标)具有新值时)。要了解有关Tracker的更多信息,请参阅Tracker manual。这是一个深入的资源,无需了解基础知识的工作原理。

它的实现方式与您在Meteor中通常接近反应性跟踪的方式不同,因为每当容器的道具发生变化时,它也需要重新运行计算。

源代码不是很长或很复杂,可以在GitHub上找到(目前here)。

  Tracker.autorun((c) => {
    if (c.firstRun) {
      //...
        data = component.getMeteorData();

    } else {
      // Stop this computation instead of using the re-run.
      // We use a brand-new autorun for each call to getMeteorData
      // to capture dependencies on any reactive data sources that
      // are accessed.  The reason we can't use a single autorun
      // for the lifetime of the component is that Tracker only
      // re-runs autoruns at flush time, while we need to be able to
      // re-call getMeteorData synchronously whenever we want, e.g.
      // from componentWillUpdate.
      c.stop();
      // Calling forceUpdate() triggers componentWillUpdate which
      // recalculates getMeteorData() and re-renders the component.
      component.forceUpdate();
    }
  })

每当计算失效(并因此重新运行)时,它就会停止计算并强制重新呈现容器,这将重新创建新计算并获得更新的数据。

这里是高级容器功能(为简洁起见,删除了一些部分):

export const ReactMeteorData = {
  componentWillMount() {
    this.data = {};
    this._meteorDataManager = new MeteorDataManager(this); // (1)
    const newData = this._meteorDataManager.calculateData(); // (2)
    this._meteorDataManager.updateData(newData); // (3)
  },

  componentWillUpdate(nextProps, nextState) {
    // backup current state and props, assign next ones to components
    let newData = this._meteorDataManager.calculateData(); // (2)
    this._meteorDataManager.updateData(newData); // (3)
    // restore backed up data
  },

  componentWillUnmount() {
    this._meteorDataManager.dispose(); // (4)
  },
};

要点是: - 在安装之前,会创建一个新的数据管理器(1)。它负责运行计算并根据数据更改填充this.data。 - 首先,每当组件更新时,计算运行(2),数据更新(3)。只要组件接收到新状态或道具(在这种类型的容器中,它应该只是道具),就会发生更新,并且正如我们之前看到的那样,当跟踪器计算失效时,由于调用了component.forceUpdate()

包裹的组件接收父级的道具,以及跟踪器计算的数据为props

return <WrappedComponent {...this.props} {...this.data} />;

关于应该如何使用的更多要点?

react-meteor-data在流星指南中有short section

通常,指南中的简单示例(以及OP的示例)应该可以正常工作,只要使用setState()正确设置状态(请参阅“它是如何工作的?”部分上文)。

此外,没有必要将容器状态重新映射到发送给孩子的道具,因为它们被传递(除非有充分的理由这样做)。

如果遇到任何性能问题,请考虑preventing re-renders部分中的要点。

从指南中:

export default ListPageContainer = withTracker(({ id }) => {
  const handle = Meteor.subscribe('todos.inList', id);
  const loading = !handle.ready();
  const list = Lists.findOne(id);
  const listExists = !loading && !!list;
  return {
    loading,
    list,
    listExists,
    todos: listExists ? list.todos().fetch() : [],
  };
})(ListPage);

在此示例中,请注意容器需要id道具,并且它也可用于包装组件,以及loadinglist等(其中)来自容器在示例中的计算。)