Я создаю приложениеact.js с потоковой архитектурой и пытаюсь выяснить, где и когда должен быть сделан запрос данных с сервера. Есть ли какой-нибудь пример для этого. (Не TODO приложение!)
Я создаю приложениеact.js с потоковой архитектурой и пытаюсь выяснить, где и когда должен быть сделан запрос данных с сервера. Есть ли какой-нибудь пример для этого. (Не TODO приложение!)
Ответы:
Я большой сторонник размещения асинхронных операций записи в создателях действий и асинхронных операций чтения в хранилище. Цель состоит в том, чтобы сохранить код модификации состояния хранилища в полностью синхронных обработчиках действий; это делает их простыми для рассуждения и простыми для модульного тестирования. Чтобы предотвратить несколько одновременных запросов к одной и той же конечной точке (например, двойное чтение), я перенесу фактическую обработку запроса в отдельный модуль, который использует обещания для предотвращения множественных запросов; например:
class MyResourceDAO {
get(id) {
if (!this.promises[id]) {
this.promises[id] = new Promise((resolve, reject) => {
// ajax handling here...
});
}
return this.promises[id];
}
}
В то время как читает в магазине включают асинхронные функции, есть важное предостережение , что в магазинах не обновлять себя в обработчиках асинхронных, но вместо того, чтобы стрелять действие и только огонь действие , когда приходит ответ. Обработчики для этого действия заканчивают тем, что делали фактическое изменение состояния.
Например, компонент может сделать:
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
}
}
flux
, вводится в магазины после строительства, поэтому нет хорошего способа получить действия в методе инициализации. Вы можете найти несколько хороших идей из изоморфных потоков Yahoo; это то, что Fluxxor v2 должен поддерживать лучше. Не стесняйтесь, напишите мне, если вы хотите поговорить об этом больше.
data: result
должно быть data : data
, верно? нет нет result
. возможно, лучше переименовать параметр данных в полезную нагрузку или что-то в этом роде.
Fluxxor имеет пример асинхронного взаимодействия с API.
Это сообщение в блоге рассказывает об этом и было размещено в блоге React.
Я считаю, что это очень важный и сложный вопрос, на который пока нет четкого ответа, поскольку синхронизация программного обеспечения внешнего интерфейса с бэкэндом все еще является проблемой.
Должны ли запросы API выполняться в компонентах JSX? Магазины? Другое место?
Выполнение запросов в магазинах означает, что если 2 магазинам нужны одни и те же данные для данного действия, они выдадут 2 похожих запроса (если вы не введете зависимости между магазинами, что мне действительно не нравится )
В моем случае мне показалось очень удобным помещать обещания Q в качестве полезных данных действий, потому что:
Аякс это ЗЛО
Я думаю, что Ajax будет все меньше и меньше использоваться в ближайшем будущем, потому что об этом очень трудно рассуждать. Правильный путь? Рассматривая устройства как часть распределенной системы, я не знаю, где я впервые наткнулся на эту идею (возможно, в этом вдохновляющем видео Криса Грейнджера ).
Подумай об этом. Теперь для масштабируемости мы используем распределенные системы с возможной согласованностью в качестве механизмов хранения (потому что мы не можем превзойти теорему CAP и часто хотим быть доступными). Эти системы не синхронизируются посредством опроса друг друга (за исключением, может быть, операций согласования?), А скорее используют структуры, такие как CRDT и журналы событий, чтобы в конечном итоге все члены распределенной системы были согласованными (участники будут сходиться к одним и тем же данным, если будет достаточно времени) ,
Теперь подумайте, что такое мобильное устройство или браузер. Это просто член распределенной системы, который может страдать из-за задержки в сети и разбиения сети. (т.е. вы используете свой смартфон в метро)
Если мы сможем создать сетевые разделы и базы данных, допускающие скорость сети (я имею в виду, что мы все еще можем выполнять операции записи в изолированный узел), мы, вероятно, сможем создать программное обеспечение внешнего интерфейса (мобильное или настольное), основанное на этих концепциях, которое будет работать в автономном режиме, поддерживаемом из коробки без функции приложения недоступен.
Я думаю, что мы должны по-настоящему вдохновиться тем, как базы данных работают над архитектурой наших веб-приложений. Стоит заметить, что эти приложения не выполняют ajax-запросы POST и PUT и GET для отправки данных друг другу, а используют журналы событий и CRDT для обеспечения возможной согласованности.
Так почему бы не сделать это на внешнем интерфейсе? Обратите внимание, что бэкэнд уже движется в этом направлении, и такие инструменты, как Kafka, широко используются крупными игроками. Это как-то связано с Event Sourcing / CQRS / DDD тоже.
Посмотрите эти удивительные статьи от авторов Кафки, чтобы убедить себя:
Возможно, мы можем начать с отправки команд на сервер и получения потока серверных событий (например, через веб-сокеты) вместо запуска Ajax-запросов.
Мне никогда не было очень удобно с запросами Ajax. Как мы, разработчики React, склонны быть функциональными программистами. Я думаю, что трудно рассуждать о локальных данных, которые должны быть вашим «источником правды» вашего веб-приложения, в то время как реальный источник правды фактически находится в базе данных сервера, а ваш «локальный» источник правды, возможно, уже устарел. когда вы его получите, и никогда не приблизитесь к реальному источнику значения истины, если не нажмете какую-нибудь хромую кнопку «Обновить» ... Это инженерия?
Однако все же немного сложно спроектировать такую вещь по некоторым очевидным причинам:
this.dispatch("LOAD_DATA", {dataPromise: yourPromiseHere});
Вы можете запросить данные либо в создателях действий, либо в магазинах. Важно не обрабатывать ответ напрямую, а создать действие в обратном вызове ошибки / успеха. Обработка ответа непосредственно в магазине приводит к более хрупкому дизайну.
Я использовал пример Binary Muse из примера Fluxor ajax . Вот мой очень простой пример, использующий тот же подход.
У меня есть простой магазин продуктов, некоторые действия с продуктом и компонент просмотра контроллера, который имеет подкомпоненты, которые все реагируют на изменения, сделанные в магазине продуктов . Например продукт слайдер , продукт-лист и продукт-поиск компоненты.
Поддельный клиент продукта
Вот фальшивый клиент, который вы могли бы заменить для вызова конечной точки, возвращающей продукты.
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% синхронными.
Я ответил на связанный с этим вопрос здесь: Как обрабатывать вложенные вызовы API в потоке
Действия не должны быть вещами, которые вызывают изменения. Предполагается, что они похожи на газету, которая информирует приложение об изменениях во внешнем мире, а затем приложение реагирует на эти новости. Магазины вызывают изменения в себе. Действия просто информируют их.
Билл Фишер, создатель Flux https://stackoverflow.com/a/26581808/4258088
То, что вы в основном должны делать, это указывать с помощью действий, какие данные вам нужны. Если магазин получает информацию о действии, он должен решить, нужно ли ему извлечь какие-либо данные.
Хранилище должно отвечать за накопление / выборку всех необходимых данных. Важно отметить, что после того, как хранилище запросило данные и получило ответ, оно должно инициировать само действие с извлеченными данными, в отличие от хранилища, обрабатывающего / сохраняющего ответ напрямую.
А магазины могут выглядеть примерно так:
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
}
}
Вот мой взгляд на это: http://www.thedreaming.org/2015/03/14/react-ajax/
Надеюсь, это поможет. :)