javascript - ejemplos - haciendo un api apacible llamada de reaccionar js
javascript tutorial (3)
Estoy haciendo un POC para la aplicación javascript isomorfa para procesar html desde el lado del servidor. El POC está trabajando con html simple, pero quiero hacer una llamada a la API y obtener la respuesta json y enviarla a la función de renderizado. Lo intenté de varias maneras pero no está funcionando. ¿Puede uno de ustedes, por favor, hacerme saber dónde me estoy perdiendo?
Soy muy nuevo para reaccionar js y cualquier ayuda será realmente apreciada.
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>
)
}
});
Deberías usar superagent , funciona muy bien para mí, también te falta la parte más importante, debes usar flux para obtener datos de un servidor, flux es la forma que Facebook recomienda, es bastante fácil de usar.
El método renderToString
de React (para representar componentes en el servidor) es síncrono. Por lo tanto, cualquier tipo de tarea asíncrona, como su solicitud de API, aún estará pendiente para cuando el componente se haya procesado.
Hay un par de maneras en que puede solucionar este problema, dependiendo de si desea obtener sus datos en el servidor o cliente.
Si elige obtener los datos en el servidor, primero mueva su lógica de api-request fuera de su componente. Luego, renderice su componente en la devolución de llamada, pasando los datos obtenidos como prop
. Se vería algo como esto:
response.on("end", function (err) {
var data = JSON.parse(buffer);
var markup = React.renderToString(Component({categories: data}));
});
Dentro de su componente, podrá acceder a los datos a través de this.props.categories
.
La otra opción es manejar la solicitud de API en el cliente. Debería realizar una solicitud AJAX en componentDidMount
y establecer el estado del componente a partir de los datos obtenidos. Se vería muy similar a lo que tiene ahora, la diferencia clave es que su lógica de solicitud residirá en componentDidMount
(async, llamado en el cliente) en lugar de componentWillMount
(no async, llamado en el servidor).
La recuperación dentro del componente utilizando componentWillMount
no es un lugar adecuado, en caso de que necesite renderizar el lado del servidor. Es necesario que, de algún modo, lo elimine, y pase los datos reales como accesorios una vez que se hayan recuperado, por ejemplo, como sugirió @JakeSendar en su respuesta.
Tengo algo de experiencia haciendo aplicaciones isomorfas con React, y el principal problema que enfrenté es cómo esperar hasta que todos los datos se cargaran antes del primer procesamiento.
Como @FakeRainBrigand ya se mencionó en los comentarios, no hay solo una forma de hacerlo, y depende de sus requisitos.
Hay algunas formas de crear una aplicación isomorfa, algunas interesantes desde mi perspectiva son: https://github.com/webpack/react-starter and http://fluxible.io/
Pero, la forma más elegante de hacerlo, como lo descubrí yo mismo, es organizar la representación asíncrona para los componentes de reacción, en particular utilizando RxJS.
En general mi aplicación está estructurada de la siguiente manera:
- vistas - Reacciona componentes sin ninguna lógica (solo una vista)
Modelos: observables con el estado actual (los datos iniciales se cargan con Superagent, luego se combinan con otros resultados de modelos y / o acciones). En el caso simple es algo como:
Rx.Observable.defer(fetchData).concat(updatesSubject).shareReplay()
Acciones (o intenciones): los observadores utilizan para recopilar las opiniones de los usuarios, hacer algo y enviar los resultados de las acciones a los modelos de los suscriptores y otras acciones. En caso simple algo como:
updatesSubject = new Rx.Subject();
action = new Rx.Subject();
action.switchMap(asyncRequest).subscribe(updatesSubject)
componentes - Observables (flujo de elementos virtuales de DOM) combinados a partir de modelos, otros componentes y acciones ( tengo una nota al respecto, explicando cómo y por qué crear elementos de Reactivos observables con RxJS ), también ahora planeo agregar componentes parciales (tupla de: componente reactivo, observables, observadores y propiedades. parcialmente lleno con el uso de DI)
enrutador: el componente responsable de manejar los cambios de ubicación, en general, la característica principal es mapear los cambios de ubicación a la secuencia de elementos virtuales de DOM y meta información. Pero en detalles, es un poco más complicado en mi caso (generación de url, resaltado de url activo, manejo de desplazamientos al navegar, también tiene la posibilidad de rutas anidadas y múltiples vistas)
Todo esto se ensambla usando un contenedor DI, en mi caso similar al contenedor angular2 DI , pero mucho más simplificado para mis necesidades específicas.
Los componentes, modelos y acciones se crean utilizando DI.
En el lado del servidor, la aplicación es así:
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)
});
});
}
y similares en el lado del cliente:
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)
});
En comparación con el flujo, es una forma más declarativa y más poderosa de organizar la aplicación. Y en el caso de la aplicación isomórfica, para mí, es mucho mejor que varios hacks con flujo. Pero, por supuesto, hay inconvenientes ... - es más complicado.
Probablemente más adelante, abriré todo esto, pero por ahora no está listo para ser publicado.
UPD1:
La respuesta original está un poco desactualizada (luego pienso actualizarla), y tengo algunos avances en esta área.
Enlaces al código mencionado anteriormente, ya abierto:
- Contenedor DI: di1
- Contenedor para componentes de reacción (vista de conexión a observables y observadores): rx-react-container
- Plantilla de inicio, para implementar widgets isomorfos, utilizando RxJS y React, y las bibliotecas anteriores: Widgets reactivos
Acerca de la solicitud completa (el trabajo aún está en progreso, y la documentación no es muy buena, pero en general debería ser clara):
- Enrutador creado especialmente para aplicaciones reactivas router1 y componentes de reacción para usarlo router1-react
- Plantilla de aplicación con enrutador y todas las bibliotecas mencionadas anteriormente: router1-app-template