【问题标题】:Meteor and withTracker: why is a component rendered twice?Meteor 和 withTracker:为什么一个组件会渲染两次?
【发布时间】:2020-06-05 16:13:13
【问题描述】:

我使用 React 创建了一个简单的 Meteor 应用程序。它在名为client 的文件夹中使用下面显示的三个文件(而不是其他文件)。在控制台中,应用程序打印出:

withTracker
rendering
withTracker
rendering
props {} {}
state null null

换句话说,App 组件被渲染了两次。最后两行输出表明 this.propsthis.state 在渲染之间都没有变化。

index.html

<body>
  <div id="react-target"></div>
</body>

main.jsx

import React from 'react'
import { render } from 'react-dom'

import App from './App.jsx'

Meteor.startup(() => {
  render(<App/>, document.getElementById('react-target'));
})

App.jsx

import React from 'react'
import { withTracker } from 'meteor/react-meteor-data'

class App extends React.Component {
  render() {
    console.log("rendering")
    return "Rendered"
  }

  componentDidUpdate(prevProps, prevState) {
    console.log("props", prevProps, this.props)
    console.log("state", prevState, this.state)
  }
}

export default withTracker(() => {
  console.log("withTracker")
})(App)

如果我将 App.jsx 更改为以下内容(删除 withTracker 包装器),则应用程序仅将 rendering 打印到控制台,并且只执行一次。

import React from 'react'
import { withTracker } from 'meteor/react-meteor-data'

export default class App extends React.Component {
  render() {
    console.log("rendering")
    return "Rendered"
  }

  componentDidUpdate(prevProps, prevState) {
    console.log(prevProps, this.props)
    console.log(prevState, this.state)
  }
}

withTracker 做了什么触发了第二次渲染?由于我无法阻止它的发生,我能否确定任何使用 withTracker 的组件都会总是呈现两次?

上下文:在我的实际项目中,我使用 withTracker 从 MongoDB 集合中读取数据,但我希望我的组件仅在 props 更改触发组件后才显示该数据重新渲染。我认为在第一次渲染后设置一个标志就足够了,但似乎我需要做一些更复杂的事情。

【问题讨论】:

    标签: meteor meteor-tracker


    【解决方案1】:

    这是一个“功能”,它不仅限于 Meteor。这是异步 javascript 的一个特性。无论您的服务器有多快,来自数据库的数据都会延迟到达。

    您的页面将立即呈现,然后在数据到达时再次呈现。您的代码需要允许这样做。

    实现此目的的一种方法是使用中间组件(它可以显示“正在加载”,直到数据到达)。假设您有一个名为 List 的组件,它将显示来自名为 MyThings 的 mongo 集合中的数据

    const Loading = (props) => {
      if (props.loading) return <div>Loading...</div>
      return <List {...props}></List>
    }
    
    export default withTracker((props) => {
      const subsHandle = Meteor.subscribe('all.myThings')
      return {
        items: MyThings.find({}).fetch(),
        loading: !subsHandle.ready(),
      }
    })(Loading)
    

    这也意味着你的List组件永远只会用数据渲染,所以它可以使用props作为初始状态,你可以将PropTypes设置为isRequired

    希望对你有帮助

    【讨论】:

    • 在我的准系统测试中,没有来自数据库的数据。 withTracker 函数没有看到任何变化,但它再次触发了渲染。在您的示例中,是的,有来自需要跟踪的数据库的数据。在您的情况下,是的,withTracker 将新道具推送到您的 Loading 组件是有意义的。所以我的问题仍然存在:为什么withTracker 会启动第二次渲染,尽管它提供的props 两次都相同?
    【解决方案2】:

    不确定您是否遇到了我发现的相同错误,或者这只是其他答案所建议的您在这里遇到的标准 React 行为,但是:

    2.0 Meteor 上运行较旧 (0.2.x) 版本的react-meteor-data 时,我看到两组不同的渲染,其中一组缺少关键的props,并导致服务器发布问题,原因是缺失的数据。考虑以下几点:

    // ./main.js
    const withSomethingCount = (C) => (props) => <C { ...props } count={ ... } />
    const withPagination = (C) => (props) => <C { ...props } pagination={ ... } />
    const withSomething = withTracker((props) => {
      console.log('withSomething:', props);
    });
    
    // Assume we're rending a "Hello, World" component here.
    export const SomeComponent = withSomethingCount(withPagination(withSomething(...)));
    
    // Console
    withSomething: { count: 0 }
    withSomething: { count: 0, pagination: { ... } }  
    withSomething: { count: 0 }
    withSomething: { count: 0, pagination: { ... } }  
    

    无论出于何种原因,我不仅看到了 N 个渲染调用,而且还看到了 N 个渲染调用,它们以重复的方式丢失了属性。对于那些阅读本文并想知道的人,组件的使用只有一种,withTracker HoC 的使用也只有一种,父 HoC 没有逻辑会导致 props 的条件传递。

    很遗憾,我还没有发现该错误的根本原因。然而,创建一个新的 Meteor 应用程序并移动代码是唯一可以消除该错误的解决方案。 Meteor 应用程序的就地更新(2.02.1)和依赖项并没有解决问题......但是全新安装并运行 git mv client imports server 确实解决了我的问题。

    很遗憾,由于 Meteor 在两年的开发过程中进行了后续更新,我不得不将其归结为某种形式的漂移。

    【讨论】:

      猜你喜欢
      • 2017-11-22
      • 2020-11-22
      • 1970-01-01
      • 2020-03-17
      • 1970-01-01
      • 1970-01-01
      • 2020-07-11
      • 2021-10-21
      • 2020-09-07
      相关资源
      最近更新 更多