Я создаю приложение response.js с архитектурой потока, и я пытаюсь выяснить, где и когда должен запрашиваться запрос данных с сервера. Есть ли какой-нибудь пример для этого. (Не приложение TODO!)
Где должен выполняться запрос ajax в приложении Flux?
Ответ 1
Я большой сторонник размещения асинхронных операций записи в создателях действий и асинхронных операций чтения в хранилище. Цель состоит в том, чтобы сохранить код модификации состояния хранилища в полностью синхронных обработчиках действий; это делает их простыми рассуждать и просты до unit test. Чтобы предотвратить одновременное одновременное обращение к одной и той же конечной точке (например, двойное чтение), я переведу фактическую обработку запроса в отдельный модуль, который использует promises для предотвращения множественных запросов; например:
class MyResourceDAO {
get(id) {
if (!this.promises[id]) {
this.promises[id] = new Promise((resolve, reject) => {
// ajax handling here...
});
}
return this.promises[id];
}
}
Во время чтения в магазине задействованы асинхронные функции, существует важное предостережение о том, что хранилища не обновляются в обработчиках async, а вместо этого запускают действие и только запускают действие, когда приходит ответ. Обработчики для этого действия в конечном итоге выполняют фактическую модификацию состояния.
Например, компонент может сделать:
getInitialState() {
return { data: myStore.getSomeData(this.props.id) };
}
В магазине будет реализован метод, возможно, что-то вроде этого:
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
}
}
Ответ 2
Fluxxor имеет пример асинхронной связи с API.
Этот пост в блоге рассказывает об этом и был включен в блог React.
Я нахожу этот очень важный и трудный вопрос, на который пока еще не ответил однозначно, поскольку синхронизация программного обеспечения интерфейса с бэкэндом по-прежнему является болью.
Должны ли выполняться запросы API в компонентах JSX? Магазины? Другое место?
Выполнение запросов в магазинах означает, что если 2 магазина нуждаются в одних и тех же данных для данного действия, они выдадут 2 похожих запроса (если вы не введете зависимости между хранилищами, которые я действительно надену, t нравится)
В моем случае, я нашел это очень удобно, чтобы положить Q promises в качестве полезной нагрузки, потому что:
- Мои действия не обязательно должны быть сериализуемыми (я не храню журнал событий, мне не нужна функция повторения событий для источника событий)
- Он устраняет необходимость иметь разные действия/события (запрос уволен/запрос завершен/запрос не выполнен) и должен сопоставлять их с помощью идентификаторов корреляции при одновременном запуске параллельных запросов.
- Это позволяет нескольким хранилищам прослушивать завершение одного и того же запроса без каких-либо зависимостей между хранилищами (однако может быть лучше ввести слой кеширования?)
Ajax - это EVIL
Я думаю, что Ajax будет в меньшей степени использоваться в ближайшем будущем, потому что это очень сложно рассуждать. Правильный путь? Рассмотрение устройств как части распределенной системы Я не знаю, где я впервые встретил эту идею (возможно, в этом вдохновляющем видео Криса Грейнджера).
Подумайте об этом. Теперь для масштабируемости мы используем распределенные системы с возможной согласованностью как механизмы хранения (потому что мы не можем победить теорему CAP, и часто мы хотим быть доступными). Эти системы не синхронизируются путем опроса друг друга (за исключением, может быть, для консенсусных операций?), А используют такие структуры, как CRDT и журналы событий, чтобы сделать все члены распределенной системы в конечном итоге последовательными (члены будут сходиться к тем же данным, учитывая достаточное время).
Теперь подумайте о том, что такое мобильное устройство или браузер. Это просто член распределенной системы, который может страдать от сетевой задержки и сетевой разбивки. (т.е. Вы используете свой смартфон в метро)
Если мы сможем построить сетевые разделы и сетевые толерантные по скорости базы данных (я имею в виду, что мы все еще можем выполнять операции записи с изолированным node), мы, вероятно, можем создавать внешние интерфейсы (мобильные или настольные), вдохновленные этими концепциями, которые хорошо работают с автономным режимом, поддерживаемым из коробки без недоступности функций приложения.
Думаю, нам нужно действительно вдохновлять нас на то, как базы данных работают над архитектурой наших приложений frontend. Одно замечание состоит в том, что эти приложения не выполняют POST и PUT и GET ajax-запросы для отправки данных друг другу, а используют журналы событий и CRDT для обеспечения конечной согласованности.
Так почему бы не сделать это на фронте? Обратите внимание, что бэкэнд уже движется в этом направлении, с инструментами, такими как Kafka, широко принятыми крупными игроками. Это так или иначе связано с Event Sourcing/CQRS/DDD.
Проверьте эти удивительные статьи от авторов Kafka, чтобы убедить себя:
- ОБРАБОТКА ПОТОКА, ИСПОЛЬЗОВАНИЕ СОБЫТИЙ, РЕАКТИВНЫЙ, КЭП... И СОДЕРЖАНИЕ СМЫСЛА ВСЕГО
- Журнал: то, что каждый инженер-программист должен знать об объединении абстракции данных в режиме реального времени.
Возможно, мы можем начать с отправки команд на сервер и получения потока событий сервера (через веб-сайты для примера) вместо того, чтобы запускать запросы Ajax.
Мне никогда не нравились запросы Ajax. Как мы реагируем разработчиков, как правило, являются функциональными программистами. Я думаю, что трудно рассуждать о локальных данных, которые, как предполагается, являются вашим "источником истины" вашего внешнего приложения, в то время как реальный источник истины на самом деле находится в базе данных сервера, а ваш "локальный" источник правды может быть уже устаревшим когда вы его получите, и никогда не сходитесь к истинному источнику истины, если вы не нажмете какую-нибудь хромовую кнопку обновления... Это инженерное дело?
Однако по некоторым очевидным причинам еще немного сложно спроектировать:
- Ваш клиент для мобильных/браузеров имеет ограниченные ресурсы и не может хранить все данные локально (поэтому иногда требуется опрос с тяжелым контентом ajax)
- Ваш клиент не должен видеть все данные распределенной системы, поэтому он должен каким-то образом фильтровать события, которые он получает по соображениям безопасности.
Ответ 3
Вы можете вызвать данные как в создателях действий, так и в магазинах. Важно не обрабатывать ответ напрямую, а создавать действие в обратном вызове ошибки/успеха. Обработка ответа непосредственно в магазине приводит к более хрупкому дизайну.
Ответ 4
Я использовал пример Binary Muse в примере Fluxxor ajax. Вот мой очень простой пример с использованием того же подхода.
У меня есть простой товарный магазин некоторые действия продукта и компонент controller-view, который имеет подкомпоненты, которые все реагируют на сделанные изменения в магазин продуктов. Например, компоненты product-slider, product-list и product-search.
Поддельный клиент продукта
Вот поддельный клиент, который вы можете заменить для вызова фактической конечной точки, возвращающей продукты.
var ProductClient = {
load: function(success, failure) {
setTimeout(function() {
var ITEMS = require('../data/product-data.js');
success(ITEMS);
}, 1000);
}
};
module.exports = ProductClient;
Магазин продуктов
Вот Магазин товаров, очевидно, это очень минимальный магазин.
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;
Теперь действия продукта, которые делают запрос AJAX и при успешном завершении действия LOAD_PRODUCTS_SUCCESS возвращают продукты в хранилище.
Действия продукта
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;
Таким образом, вызов this.getFlux().actions.productActions.loadProducts()
из любого компонента, прослушивающего этот магазин, будет загружать продукты.
Вы можете представить себе разные действия, которые могли бы реагировать на взаимодействия с пользователем, такие как addProduct(id)
removeProduct(id)
и т.д.... по одному и тому же шаблону.
Надеюсь, что этот пример немного помогает, поскольку я нашел это немного сложным для реализации, но, безусловно, помог сохранить мои магазины на 100% синхронно.
Ответ 5
Я ответил на соответствующий вопрос здесь: Как обрабатывать вложенные вызовы api в потоке
Действия не должны быть вещами, которые вызывают изменение. Они должны быть похожи на газету, которая информирует о применении изменений во внешнем мире, а затем приложение реагирует на эти новости. Магазины создают изменения сами по себе. Действия просто информируют их.
Билл Фишер, создатель Flux fooobar.com/questions/34681/...
То, что вы в основном должны делать, заключается в том, чтобы указать, какие данные вам нужны. Если магазин получает информацию о действии, он должен решить, нужно ли ему извлекать некоторые данные.
Магазин должен нести ответственность за накопление/выборку всех необходимых данных. Важно отметить, однако, что после того, как магазин запросил данные и получил ответ, он должен инициировать действие непосредственно с извлеченными данными, в противоположность обработке хранилища/сохранению ответа напрямую.
Магазины могут выглядеть примерно так:
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
}
}
Ответ 6
Здесь я беру на себя следующее: http://www.thedreaming.org/2015/03/14/react-ajax/
Надеюсь, что это поможет.:)