避免withTracker内部的ReactiveVar和Meteor.call无限循环?

时间:2018-07-31 01:21:50

标签: javascript reactjs meteor

当前,我正在尝试使用功能性StateLess组件,因为对于StoryStor来说,它们似乎更容易测试/模拟/分开。

我已经开始使用withTracker以便将React组件与Meteor集成在一起,并且在使用Meteor.subscribe时一切正常,例如:

...
module.exports = withTracker( (props) => {
  subscription = Meteor.subscribe( 'posts' )

  loading = subscription.ready()
  posts = Posts.find({}).fetch()

  return {loading, posts}
} )( LowerLevelComponent )
...

但是有时候我需要使用Meteor.call使它成为Reactive,

...
module.exports = withTracker( (props) => {
  feed = new ReactiveVar(null)

  Meteor.call( 'feed', (error, response) => {

    work = // do some work

    feed.set( work )
  } )

  loading = subscription.ready()
  feed = feed.get()

  return {loading, feed}
} )( LowerLevelComponent )
...

这里的问题是,每次此组件运行时,都会将变量“ feed”再次分配给ReactiveVar,然后再次调用Meteor.call,并开始无限循环。


我发现的唯一解决方案是使用“提要”作为功能组件中的ReactiveVar,例如:

feed = new ReactiveVar(null)
module.exports = withTracker( (props) => {

  if( feed.get() == null ) {
    Meteor.call( 'feed', (error, response) => {

      work = // do some work

      feed.set( work )
    } )
  }

  loading = subscription.ready()
  feed = feed.get()

  return {loading, feed}
} )( LowerLevelComponent )

这里出现的问题是:

  • 如果我浏览路由器,然后返回此页面,该值仍将填充ReactiveVar或withTracker确保它已从内存中销毁了怎么办?

  • 如果我想拥有两个这样的组件但加载不同的提要怎么办?我是否必须为范围之外的变量使用“动态”名称?这似乎很hacky。我看到有些人使用Session来存储这些东西,但这听起来更加骇人听闻。

  • 理想情况下,我会将ReactiveVar / Meteor.call逻辑存储在什么地方,并使它仍然属于我组件的特定实例?

  • 这是“状态”的全部内容,我应该使用一些React组件让我setState吗?从我糟糕的React经验来看,最好不要使用状态,因此可以在StoryBook / Jest /任何需要使用的测试框架上轻松地测试代码?

通过查看源代码实现here,我可以看到它将this.props和this.data发送到较低级组件。this.data是技巧吗?是我应该添加我的ReactiveVar的地方,以便我可以对其进行跟踪并仍然对该实例保持唯一性吗?


阅读@Fred Stark回复后:

  

因此,这里的主要问题是通过使用reactVar,您正在向组件引入状态。这使功能性无状态组件成为表示您要执行的操作的错误模式选择。在这种情况下,尝试将类模式与React.Component一起使用

我得出的结论是, 的主要问题不是我正在向组件引入状态,但实际上,这里的主要问题是Meteor.call的方式()的行为与Meteor.subscribe()的行为不同。

如果将“状态添加到函数无状态对象”是一个实际问题,那么withTracker函数将完全没有意义。 Meteor.subscribe()确实向FSC添加了状态,这是在Meteor Guide

上看到的将Meteor数据与React集成的推荐方法之一

得出该结论后,我在网上进行了更多搜索,并意识到有一些尝试解决此问题的实现,例如meteor-callReactiveMethod。这些库的潜在功能将使我能够以“包含”方式隐藏变通方法,并使Meteor.call的工作方式与Meteor.subscribe()相似。

其他选择可能是不使用Meteor.call来获取数据,即使它不会是被动的,但我不确定这会产生哪些副作用。

1 个答案:

答案 0 :(得分:0)

因此,经过几天的努力,我终于找到了一种解决方案,该解决方案不会破坏我要遵循的某些限制/规则,以使我的组件兼容并在我的网站上以及在故事书。

诀窍是创建一个不与withTracker一起使用的包装函数,而该函数将在发生反应性事件时保留此“非反应性”作用域,所有这些都具有以下优点:一旦将我的组件从屏幕上删除,便会“自动”创建(并希望!)并销毁它。

代替:

feed = new ReactiveVar(null)
module.exports = withTracker( (props) => {

  if( feed.get() == null ) {
    Meteor.call( 'feed', (error, response) => {

      work = // do some work

      feed.set( work )
    } )
  }

  loading = subscription.ready()
  feed = feed.get()

  return {loading, feed}
} )( LowerLevelComponent )

我正在做:

module.exports = (props) => {

  // NOTE: this will create a ReactiveVar everytime my HOC is created, 
  // without using withTracker.
  feed = new ReactiveVar(null)

  // The "reactive data" will live on my "Intermediary Order Object"
  // which uses withTracker and will be reactive!
  Intermediary = withTracker( (props) => {

    if( feed.get() == null ) {
      Meteor.call( 'feed', (error, response) => {

        work = // do some work

        feed.set( work )
      } )
    }

    loading = subscription.ready()
    feed = feed.get()

    return {loading, feed}
  } )( LowerLevelComponent )

  <Intermediary {...props} />