流星,服务器渲染withTracker。推迟客户端呈现

时间:2018-02-01 16:36:38

标签: meteor server-rendering

在我的数据库中,我有一系列动物,我想将它们渲染成一个漂亮的小清单。为了改善用户体验,我想在服务器上呈现它(使用新的server-render包),然后使用react-meteor-datawithTracker)订阅任何更改。

现在,这是有效的,除了一件事。服务器按预期呈现内容(包括数据),然后将其发送到客户端。问题出在客户端上。

页面加载后,meteor设置数据连接,然后呈现页面。第一次渲染发生在数据连接返回任何数据之前,因此它呈现一个空的动物列表(覆盖服务器上呈现的列表并引发警告)。然后,一旦数据到达,列表就会被完全(重新)渲染。

这会导致相当糟糕的用户体验,因为列表会闪烁然后返回。我想推迟客户端呈现,直到数据可用。这可能吗?

我的代码很简单,看起来像这样:

列出组件:

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

import { AnimalsData } from '../api/animals';

class Animals extends Component {
    render() {
        const {animals} = this.props;
        console.log(animals);

        return <ul>
            {animals.map(animal =>
                <li key={animal._id}>
                    {animal.name}
                </li>)
            }
        </ul>
    }
};

// Load data into props, subscribe to changes on the client
export default withTracker(params => {
    if (Meteor.isClient) {
        // No need to subscribe on server (this would cause an error)
        Meteor.subscribe('animals');
    }

    return {
        animals: AnimalsData.find({}).fetch()
    };
})(Animals);

服务器

import React from "react";
import { renderToString } from "react-dom/server";
import { onPageLoad } from "meteor/server-render";

import Animals from '../imports/ui/Animals';
import '../imports/api/animals';

onPageLoad((sink) => {
    sink.renderIntoElementById('app', renderToString(<Animals />));
});

客户端:

import React from 'react';
import ReactDOM from "react-dom";
import { onPageLoad } from "meteor/server-render";

import AnimalList from '../imports/ui/Animals';

onPageLoad(sink => {
    ReactDOM.hydrate(
        <AnimalList />,
        document.getElementById("app")
    );
});

数据库:

import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';

export const AnimalsData = new Mongo.Collection('animals');

if (Meteor.isServer) {

    Meteor.publish('animals', () => {
        return AnimalsData.find({});
    });
}

会发生什么(Animals.jsx中的console.log):

  1. 在服务器上呈现[动物数据]
  2. 在数据到达之前在客户端上呈现。这将删除在服务器[]
  3. 上呈现的列表
  4. 数据到达时在客户端呈现[动物数据]

3 个答案:

答案 0 :(得分:1)

您可以延迟为页面充水,直到您的订阅准备就绪。

例如,假设您有一组链接

import { Mongo } from 'meteor/mongo';
import { Meteor } from 'meteor/meteor';

export default Links = new Mongo.Collection('links');

if(Meteor.isServer) {
  Meteor.publish('links', () => {
    return Links.find({});
  });
}

client/main.js中,您将订阅该出版物,并等待其准备好后才能继续进行水合作用。您可以使用meteor/tracker来做到这一点,因为ready()是可以观察到的。

import React from 'react';
import ReactDOM from 'react-dom';
import { Meteor } from 'meteor/meteor';
import { onPageLoad } from "meteor/server-render";
import App from '../imports/ui/entry_points/ClientEntryPoint';
import { Tracker } from 'meteor/tracker';

onPageLoad(async sink => {
  Tracker.autorun(computation => {
    if(Meteor.subscribe('links').ready()) {
      ReactDOM.hydrate(
        <App />,
        document.getElementById("react-target")
      );
      computation.stop();
    }
  })
});

很明显,这需要订阅整个应用程序中的所有内容,但是您可以添加其他逻辑来根据路线订阅不同的内容。

答案 1 :(得分:0)

我为您创建了一个包,可以防止在页面加载时重新呈现组件。

在此处查看https://github.com/pravdomil/Meteor-React-SSR-and-CSR-with-loadable-and-subscriptions

答案 2 :(得分:-1)

您可以使用.ready()在订阅尚未准备好时不呈现,例如:

const animalSub = Meteor.subscribe('animals')

if (animalSub.ready()) {
  return AnimalsData.find().fetch()
}