Estoy creando una aplicación react.js con arquitectura de flujo y estoy tratando de averiguar dónde y cuándo se debe hacer una solicitud de datos del servidor. ¿Hay algún ejemplo para esto? (¡No TODO aplicación!)
Estoy creando una aplicación react.js con arquitectura de flujo y estoy tratando de averiguar dónde y cuándo se debe hacer una solicitud de datos del servidor. ¿Hay algún ejemplo para esto? (¡No TODO aplicación!)
Respuestas:
Soy un gran defensor de poner operaciones de escritura asíncrona en los creadores de acción y operaciones de lectura asíncrona en la tienda. El objetivo es mantener el código de modificación del estado de la tienda en controladores de acción totalmente sincrónicos; esto los hace simples de razonar y de prueba de unidad simple. Para evitar múltiples solicitudes simultáneas al mismo punto final (por ejemplo, lectura doble), moveré el procesamiento de solicitud real a un módulo separado que usa promesas para evitar las solicitudes múltiples; por ejemplo:
class MyResourceDAO {
get(id) {
if (!this.promises[id]) {
this.promises[id] = new Promise((resolve, reject) => {
// ajax handling here...
});
}
return this.promises[id];
}
}
Si bien las lecturas en la tienda implican funciones asincrónicas, existe una advertencia importante de que las tiendas no se actualizan en los controladores asincrónicos, sino que activan una acción y solo activan una acción cuando llega la respuesta. Los controladores de esta acción terminan haciendo la modificación de estado real.
Por ejemplo, un componente podría hacer:
getInitialState() {
return { data: myStore.getSomeData(this.props.id) };
}
La tienda tendría un método implementado, tal vez, algo como esto:
class Store {
getSomeData(id) {
if (!this.cache[id]) {
MyResurceDAO.get(id).then(this.updateFromServer);
this.cache[id] = LOADING_TOKEN;
// LOADING_TOKEN is a unique value of some kind
// that the component can use to know that the
// value is not yet available.
}
return this.cache[id];
}
updateFromServer(response) {
fluxDispatcher.dispatch({
type: "DATA_FROM_SERVER",
payload: {id: response.id, data: response}
});
}
// this handles the "DATA_FROM_SERVER" action
handleDataFromServer(action) {
this.cache[action.payload.id] = action.payload.data;
this.emit("change"); // or whatever you do to re-render your app
}
}
flux
se inyecta en las tiendas después de la construcción, por lo que no hay una excelente manera de obtener acciones en el método de inicialización. Puede encontrar algunas buenas ideas de las bibliotecas de flujo isomoróficas de Yahoo; Esto es algo que Fluxxor v2 debería admitir mejor. No dude en enviarme un correo electrónico si desea hablar más sobre esto.
data: result
debería ser data : data
, ¿verdad? No existe result
. tal vez sea mejor cambiar el nombre del parámetro de datos a la carga útil o algo así.
Fluxxor tiene un ejemplo de comunicación asíncrona con una API.
Esta publicación de blog habla de ello y ha aparecido en el blog de React.
Considero que esta es una pregunta muy importante y difícil que aún no se ha respondido claramente, ya que la sincronización del software de la interfaz con el back-end sigue siendo difícil.
¿Deben realizarse solicitudes de API en componentes JSX? ¿Historias? ¿Otro lugar?
Realizar solicitudes en tiendas significa que si 2 tiendas necesitan los mismos datos para una acción determinada, emitirán 2 solicitudes similares (a menos que introduzca dependencias entre tiendas, lo que realmente no me gusta )
En mi caso, he encontrado esto muy útil para poner Q promesas como carga de acciones porque:
Ajax es MAL
Creo que Ajax se usará cada vez menos en el futuro cercano porque es muy difícil razonar sobre ello. ¿La direccion correcta? Considerando los dispositivos como parte del sistema distribuido, no sé dónde encontré esta idea por primera vez (tal vez en este inspirador video de Chris Granger ).
Piénsalo. Ahora, para la escalabilidad, utilizamos sistemas distribuidos con eventual consistencia como motores de almacenamiento (porque no podemos superar el teorema de CAP y, a menudo, queremos estar disponibles). Estos sistemas no se sincronizan mediante encuestas entre sí (¿excepto quizás para operaciones de consenso?) Sino que usan estructuras como CRDT y registros de eventos para que todos los miembros del sistema distribuido sean eventualmente consistentes (los miembros convergerán a los mismos datos, con el tiempo suficiente) .
Ahora piense en lo que es un dispositivo móvil o un navegador. Es solo un miembro del sistema distribuido que puede sufrir latencia de red y particionamiento de red.(es decir, está utilizando su teléfono inteligente en el metro)
Si podemos construir particiones de red y bases de datos tolerantes a la velocidad de la red (quiero decir que aún podemos realizar operaciones de escritura en un nodo aislado), probablemente podamos construir softwares frontend (móviles o de escritorio) inspirados en estos conceptos, que funcionan bien con el modo fuera de línea admitido de la caja sin las características de la aplicación indisponibilidad.
Creo que realmente deberíamos inspirarnos sobre cómo funcionan las bases de datos para diseñar nuestras aplicaciones frontend. Una cosa a tener en cuenta es que estas aplicaciones no realizan solicitudes ajax POST y PUT y GET para enviarse datos entre sí, sino que usan registros de eventos y CRDT para garantizar la coherencia eventual.
Entonces, ¿por qué no hacer eso en la interfaz? Tenga en cuenta que el backend ya se está moviendo en esa dirección, con herramientas como Kafka masivamente adoptadas por grandes jugadores. Esto de alguna manera también está relacionado con el Abastecimiento de eventos / CQRS / DDD.
Consulte estos impresionantes artículos de autores de Kafka para convencerse:
Tal vez podamos comenzar enviando comandos al servidor y recibiendo una secuencia de eventos del servidor (a través de sockets web, por ejemplo), en lugar de disparar solicitudes de Ajax.
Nunca me he sentido muy cómodo con las solicitudes de Ajax. A medida que reaccionamos, los desarrolladores tienden a ser programadores funcionales. Creo que es difícil razonar sobre los datos locales que se supone que son su "fuente de verdad" de su aplicación frontend, mientras que la verdadera fuente de verdad está realmente en la base de datos del servidor, y su fuente de verdad "local" ya puede estar desactualizada cuando lo reciba, y nunca convergerá a la fuente real de valor de verdad a menos que presione algún botón de actualización cojo ... ¿Es esto ingeniería?
Sin embargo, todavía es un poco difícil diseñar tal cosa por algunas razones obvias:
this.dispatch("LOAD_DATA", {dataPromise: yourPromiseHere});
Puede solicitar datos en los creadores de acciones o en las tiendas. Lo importante es no manejar la respuesta directamente, sino crear una acción en la devolución de llamada de error / éxito. Manejar la respuesta directamente en la tienda conduce a un diseño más frágil.
He estado usando el ejemplo de Binary Muse del ejemplo de Fluxxor ajax . Aquí está mi ejemplo muy simple usando el mismo enfoque.
Tengo una tienda de productos simple , algunas acciones de productos y el componente de vista de controlador que tiene subcomponentes que responden a los cambios realizados en la tienda de productos . Por ejemplo producto deslizante , producto de lista y de búsqueda de productos componentes.
Cliente de producto falso
Aquí está el cliente falso que puede sustituir por llamar a un punto final real que devuelve productos.
var ProductClient = {
load: function(success, failure) {
setTimeout(function() {
var ITEMS = require('../data/product-data.js');
success(ITEMS);
}, 1000);
}
};
module.exports = ProductClient;
Tienda de productos
Aquí está la Tienda de productos, obviamente, esta es una tienda muy mínima.
var Fluxxor = require("fluxxor");
var store = Fluxxor.createStore({
initialize: function(options) {
this.productItems = [];
this.bindActions(
constants.LOAD_PRODUCTS_SUCCESS, this.onLoadSuccess,
constants.LOAD_PRODUCTS_FAIL, this.onLoadFail
);
},
onLoadSuccess: function(data) {
for(var i = 0; i < data.products.length; i++){
this.productItems.push(data.products[i]);
}
this.emit("change");
},
onLoadFail: function(error) {
console.log(error);
this.emit("change");
},
getState: function() {
return {
productItems: this.productItems
};
}
});
module.exports = store;
Ahora las acciones del producto, que hacen la solicitud AJAX y, en caso de éxito, activan la acción LOAD_PRODUCTS_SUCCESS que devuelve los productos a la tienda.
Acciones de producto
var ProductClient = require("../fake-clients/product-client");
var actions = {
loadProducts: function() {
ProductClient.load(function(products) {
this.dispatch(constants.LOAD_PRODUCTS_SUCCESS, {products: products});
}.bind(this), function(error) {
this.dispatch(constants.LOAD_PRODUCTS_FAIL, {error: error});
}.bind(this));
}
};
module.exports = actions;
Entonces llamando this.getFlux().actions.productActions.loadProducts()
desde cualquier componente que escuche esta tienda cargaría los productos.
Sin embargo, podría imaginarse teniendo diferentes acciones que responderían a las interacciones del usuario como addProduct(id)
removeProduct(id)
etc. siguiendo el mismo patrón.
Espero que ese ejemplo ayude un poco, ya que me pareció un poco difícil de implementar, pero ciertamente ayudó a mantener mis tiendas 100% sincronizadas.
Respondí una pregunta relacionada aquí: Cómo manejar llamadas api anidadas en flujo
Se supone que las acciones no son cosas que causen un cambio. Se supone que son como un periódico que informa la aplicación de un cambio en el mundo exterior, y luego la aplicación responde a esa noticia. Las tiendas provocan cambios en sí mismas. Las acciones solo les informan.
Bill Fisher, creador de Flux https://stackoverflow.com/a/26581808/4258088
Lo que básicamente debería estar haciendo es, indicando a través de acciones qué datos necesita. Si la acción informa a la tienda, debería decidir si necesita obtener algunos datos.
La tienda debe ser responsable de acumular / recuperar todos los datos necesarios. Sin embargo, es importante tener en cuenta que después de que la tienda solicitó los datos y obtiene la respuesta, debería desencadenar una acción en sí misma con los datos recuperados, en lugar de que la tienda maneje / guarde la respuesta directamente.
Una tienda podría verse así:
class DataStore {
constructor() {
this.data = [];
this.bindListeners({
handleDataNeeded: Action.DATA_NEEDED,
handleNewData: Action.NEW_DATA
});
}
handleDataNeeded(id) {
if(neededDataNotThereYet){
api.data.fetch(id, (err, res) => {
//Code
if(success){
Action.newData(payLoad);
}
}
}
}
handleNewData(data) {
//code that saves data and emit change
}
}
Aquí está mi opinión sobre esto: http://www.thedreaming.org/2015/03/14/react-ajax/
Espero que ayude. :)