Je fais un POC pour une application javascript isomorphe pour rendre le HTML du côté serveur. Le POC fonctionne avec du html simple, mais je veux faire un appel api et obtenir la réponse json et l'envoyer à la fonction de rendu. J'ai essayé différentes manières mais cela ne fonctionne pas. Pouvez-vous, s'il vous plaît, me faire savoir où je manque?.
Je suis très nouveau pour réagir js et toute aide sera vraiment appréciée
loadCategoriesFromServer: function() {
var self = this;
// get walking directions from central park to the empire state building
var http = require("http");
url = "api url here";
var request = http.get(url, function (response) {
// data is streamed in chunks from the server
// so we have to handle the "data" event
var buffer = "",
data,
route;
response.on("data", function (chunk) {
buffer += chunk;
});
response.on("end", function (err) {
data = JSON.parse(buffer);
//console.log(data.d);
//console.log(data.d.Items);
self.setState({
categories: data.d.Items
});
});
});
}, // load from server end
getInitialState: function() {
return { categories: [] };
},
componentWillMount: function() {
console.log("calling load categories")
this.loadCategoriesFromServer();
},
render: function () {
//console.log("data");
//console.log(this.state.categories);
var postNodes = this.state.categories.map(function (cat) {
console.log(cat);
});
return (
<div id="table-area">
//i want to Paint the data here..
</div>
)
}
});
La récupération à l'intérieur du composant à l'aide de componentWillMount
n'est pas un bon endroit, au cas où vous auriez besoin de rendre côté serveur. Vous devez en quelque sorte le déplacer hors du composant de formulaire et transmettre les données réelles en tant qu'accessoires après leur récupération - par exemple, comme @JakeSendar l'a suggéré dans sa réponse.
J'ai une certaine expérience de l'application isomorphe avec React, et le principal problème que j'ai rencontré est de savoir comment attendre que toutes les données soient chargées avant le premier rendu
Comme @FakeRainBrigand l'a déjà mentionné dans les commentaires, il n'y a pas qu'une seule façon de le faire, et cela dépend de vos besoins.
Il existe peu de façons de créer une application isomorphe, les plus intéressantes de mon point de vue sont: https://github.com/webpack/react-starter et http: // fluxible. io /
Mais la façon la plus élégante de le faire, comme je l'ai compris par moi-même, est d'organiser le rendu asynchrone des composants React, en particulier en utilisant RxJS.
En général, ma candidature est structurée comme suit:
modèles - Observables avec l'état actuel (les données initiales sont chargées à l'aide de superagent, puis combinées avec d'autres résultats de modèles et/ou d'actions). Dans le cas simple, c'est quelque chose comme:
Rx.Observable.defer(fetchData).concat(updatesSubject).shareReplay()
actions (ou intentions) - Observateurs utilisés pour collecter les commentaires des utilisateurs, faire quelque chose et envoyer les résultats des actions aux modèles d'abonnés et/ou à d'autres actions. Dans le cas simple, quelque chose comme:
updatesSubject = new Rx.Subject();
action = new Rx.Subject();
action.switchMap(asyncRequest).subscribe(updatesSubject)
composants - Observables (flux d'éléments DOM virtuels) combinés à partir de modèles, d'autres composants et actions ( J'ai une note à ce sujet, expliquant comment et pourquoi créer des éléments Observable React avec RxJS) ), je prévois également d'ajouter des composants partiels (Tuple de: react component, observables, observers, and properties. partiellement rempli avec l'aide de DI)
routeur - composant responsable de la gestion des changements d'emplacement, en général, la caractéristique principale consiste à mapper les changements d'emplacement au flux d'éléments DOM virtuels et aux métadonnées. Mais dans les détails, c'est un peu plus compliqué dans mon cas (génération d'URL, mise en évidence d'URL active, gestion des parchemins lors de la navigation, il a également la possibilité de routes imbriquées et de plusieurs vues)
Tout cela est assemblé à l'aide d'un conteneur DI, dans mon cas similaire à conteneur DI angular2 , mais beaucoup simplifié pour mes besoins spécifiques.
Les composants, modèles et actions sont créés à l'aide de DI.
Du côté serveur, l'application est comme ceci:
var rootInjector = new Injector();
// setup server specific providers
rootInjector.provide(..., ...)
app.get('/*', function(req,res){
var injector = rootInjector.createChild();
// setup request specific providers
injector.provide(..., ...);
injector.get(Router)
.first()
.subscribe(function(routingResult){
res.render('app', {
title: routingResult.title,
content: React.renderToString(routingResult.content)
});
});
}
et similaire côté client:
var rootInjector = new Injector();
// setup server specific providers
// actually this is omitted in my case because default providers are client side
rootInjector.provide(..., ...)
contentElement = document.getElementById('#content');
rootInjector.get(Router)
.subscribe(function(routingResult){
document.title = routingResult.title;
React.render(routingResult.content, contentElement)
});
Par rapport au flux, c'est un moyen plus déclaratif et plus puissant d'organiser l'application. Et en cas d'application isomorphe - pour moi, il semble beaucoup mieux que divers hacks avec flux. Mais bien sûr, il y a des inconvénients ... - c'est plus compliqué.
Probablement plus tard, j'ouvrirai tout cela, mais pour l'instant - il n'est pas tout à fait prêt à être publié.
UPD1:
La réponse originale est un peu dépassée (plus tard, je prévois de la mettre à jour), et j'ai des progrès dans ce domaine.
Liens vers le code mentionné ci-dessus, déjà ouverts:
A propos de l'application complète (le travail est toujours en cours et la documentation n'est pas très bonne, mais en général, elle devrait être claire):
La méthode renderToString
de React (pour le rendu des composants sur le serveur) est synchrone. Par conséquent, toute sorte de tâche asynchrone, telle que votre demande d'api, sera toujours en attente au moment du rendu du composant.
Il existe plusieurs façons de résoudre ce problème, selon que vous souhaitez ou non récupérer vos données sur le serveur ou le client.
Si vous choisissez de récupérer les données sur le serveur, déplacez d'abord votre logique de demande d'api en dehors de votre composant. Ensuite, rendez votre composant dans le rappel, en passant les données récupérées sous la forme d'un prop
. Cela ressemblerait à quelque chose comme ceci:
response.on("end", function (err) {
var data = JSON.parse(buffer);
var markup = React.renderToString(Component({categories: data}));
});
Dans votre composant, vous pourrez accéder aux données via this.props.categories
.
L'autre option consiste à gérer la demande d'api sur le client. Vous feriez une demande AJAX dans componentDidMount
et définir l’état du composant à partir des données récupérées. Il ressemblerait beaucoup à ce que vous avez maintenant, la principale différence étant que votre la logique de demande vivrait dans componentDidMount
(async, appelé sur le client) plutôt que componentWillMount
(pas async, appelé sur le serveur).
Vous devez utiliser superagent , fonctionne très bien pour moi, vous manquez également la partie la plus importante, vous devez utiliser flux pour récupérer des données à partir d'un serveur, flux est la façon que Facebook recommande fortement, il est assez facile à utiliser architecture de flux .