【问题标题】:Avoiding infinite loop with ReactiveVar and Meteor.call inside of withTracker?在 withTracker 中使用 ReactiveVar 和 Meteor.call 避免无限循环?
【发布时间】:2018-08-01 05:20:21
【问题描述】:

目前我正在尝试使用功能性无状态组件,因为它们似乎更容易为 StoryBook 测试/模拟/保持独立。

我已经开始使用 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 并开始无限循环。


我找到的唯一解决方案是使用“feed”作为功能组件之外的 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 组件吗?从我糟糕的 React 经验看来,最好不要从不使用状态,这样代码就可以在 StoryBook / Jest / 任何需要使用的测试框架上轻松测试?

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


看完@Fred Stark 的回复:

所以这里的主要问题是,通过使用 reactiveVar 您正在向组件引入状态。这使得功能性无状态组件成为表示您正在尝试做的事情的模式的糟糕选择。尝试在这种情况下使用带有 React.Component 的类模式

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

如果将“状态添加到函数无状态对象”是一个真正的问题,那么 withTracker 函数将没有意义。 Meteor.subscribe() 确实向 FSC 添加了状态,这是将 Meteor 数据与 React 集成的推荐方法之一,如 Meteor Guide

所示

在得出这个结论后,我在网上搜索了一下,发现有一些实现试图解决这个问题,例如meteor-call 和ReactiveMethod。这些库可能允许我以“包含”的方式隐藏解决方法,并使 Meteor.calls 的工作方式类似于 Meteor.subscribe()。

其他选项可能是不使用 Meteor.call 来获取数据,即使它不会是反应性的,但我不能 100% 确定这可能产生的副作用。

【问题讨论】:

  • 所以这里的主要问题是通过使用reactiveVar 您正在向组件引入状态。这使得功能性无状态组件成为表示您正在尝试做的事情的模式的糟糕选择。尝试在这种情况下使用带有 React.Component 的类模式
  • @FredStark 非常感谢您的回复,它让我思考并重新考虑了此实施的一些关键点,并找到了其他一些解决方案,我已经相应地编辑了我的问题!不要忘记我的主要目标只是获取数据并将其提供给我的 LowerLevelComponent,这是我的 100% 无状态 UI。将 HOC 保留为 1 个函数文件有助于我们保持文件小且项目可测试,至少根据我们团队的经验,使用类使模拟和测试变得更加困难。谢谢
  • 好点不是真的是状态。我不确定最好的解决方案!否则我会自己添加一个答案。顺便问一下,这些都是很好的问题!
  • 您可以利用当前用户也可以附加字段这一事实。这就是我们如何设法检查用户是否已经查看了多个组件或以持久的方式自定义他们的 UI。当然,这可以使用集合来完成,但这会引入新的依赖关系。由于您的用户已经是依赖项,因此您可以保持尽可能灵活和无状态。我知道这也不是 100% 的解决方案,而是某种妥协。

标签: javascript reactjs meteor


【解决方案1】:

因此,经过几天的摸索,我终于找到了一个解决方案,它不会破坏我遵循的一些限制/规则,以便在我的网站和网站上兼容和测试我的组件故事书。

诀窍是创建一个不使用 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} />      

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-07
    • 1970-01-01
    • 2019-05-01
    • 2022-01-15
    • 2015-05-23
    • 1970-01-01
    相关资源
    最近更新 更多