在我的数据库中,我有一系列动物,我想将它们渲染成一个漂亮的小清单。为了改善用户体验,我想在服务器上呈现它(使用新的server-render
包),然后使用react-meteor-data
(withTracker
)订阅任何更改。
现在,这是有效的,除了一件事。服务器按预期呈现内容(包括数据),然后将其发送到客户端。问题出在客户端上。
页面加载后,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):
答案 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()
}