Де слід надсилати запит на Ajax у додатку Flux?


194

Я створюю додаток react.js з архітектурою потоку, і я намагаюся зрозуміти, де і коли слід робити запит на дані з сервера. Чи є приклад для цього. (Не додаток TODO!)

Відповіді:


127

Я великий прихильник введення операцій запису асинхронізу в творці дій та асинхронних операцій читання в магазині. Метою є збереження коду зміни стану зберігання у повністю синхронних обробниках дій; це робить їх простими в обґрунтуванні і простими в одиничному тесті. З метою запобігання декількох одночасних запитів до однієї кінцевої точки (наприклад, подвійне читання), я переміщу фактичну обробку запиту в окремий модуль, який використовує обіцянки для запобігання декількох запитів; наприклад:

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
  }
}

Ви намагалися поставити обіцянки всередині корисних навантажень дій? Мені легше впоратися, ніж відправити кілька дій
Себастьєн Лорбер

@SebastienLorber Для мене великим притягненням є збереження всіх оновлень стану синхронним кодовим шляхом і явно лише в результаті розсилки дій, тому я уникаю асинхронності всередині магазинів.
Мішель Тіллі

1
@Federico Мені все одно незрозуміло, що таке "найкраще" рішення. Я експериментував із цією стратегією для завантаження даних у поєднанні з підрахунком кількості невирішених запитів на асинхронізацію. На жаль, fluxйого вводять у магазини після будівництва, тому немає жодного чудового способу отримати дії в методі ініціалізації. Ви можете знайти кілька хороших ідей від ізоморофних потоків вух Yahoo; це те, що Fluxxor v2 має підтримувати краще. Не соромтеся написати мені електронну пошту, якщо ви хочете поговорити про це більше.
Мішель Тіллі

1
data: resultмає бути data : data, правда? немає result. можливо краще перейменувати параметр даних для корисного навантаження або щось подібне.
олігофрен

2
Я вважав цю стару нитку дуже корисною - зокрема, коментарі Білла Фішера та Цзіна Чена. Це дуже близько до того, що пропонує @BinaryMuse з незначною різницею, що диспетчеризація відбувається у творця дій.
phillipwei

37

Fluxxor має приклад зв'язку з асинхронністю з API.

Ця публікація в блозі розповідає про це і була розміщена в блозі React.


Я вважаю це дуже важливим і складним питанням, на яке ще не знайдено чіткого відповіді, оскільки синхронізація програмного забезпечення в інтерфейсі із заднім днем ​​все ще біль.

Чи слід робити запити API в компоненти JSX? Магазини? Інше місце?

Виконання запитів у магазинах означає, що якщо 2 магазинам потрібні однакові дані для певної дії, вони видадуть 2 подібних запиту (якщо ви не введете залежності між магазинами, що мені дуже не подобається )

У моєму випадку я вважаю це дуже зручним, щоб ставити обіцянки Q як корисне навантаження дій, оскільки:

  • Мої дії не повинні бути серіалізаційними (я не зберігаю журнал подій, мені не потрібна функція повторної події для пошуку джерел подій)
  • Це знімає необхідність в різних діях / подіях (запит запущений / запит виконаний / запит не виконаний) і повинен відповідати їм за допомогою ідентифікаторів кореляції, коли одночасні запити можуть бути відпущені.
  • Це дозволяє багатьом магазинам прослуховувати завершення одного і того ж запиту, не вводячи ніякої залежності між магазинами (однак, можливо, краще ввести шар кешування?)

Аякс - ЗЛИВ

Я думаю, що Ajax найближчим часом буде все менше використовуватися, тому що це дуже важко міркувати. Правильний шлях? Розглядаючи пристрої як частину розподіленої системи, я не знаю, де я вперше зіткнувся з цією ідеєю (можливо, у цьому надихаючому відео Кріса Грейнджера ).

Подумай над цим. Тепер для масштабованості ми використовуємо розподілені системи з можливою послідовністю як двигуни зберігання (оскільки ми не можемо перемогти теорему CAP і часто хочемо бути доступними). Ці системи не синхронізуються через опитування один одного (за винятком можливо консенсусних операцій?), А скоріше використовують структури, такі як CRDT та журнали подій, щоб змусити всіх членів розподіленої системи в кінцевому підсумку (члени зближуватимуться з тими ж даними, даючи достатньо часу) .

Тепер подумайте, що таке мобільний пристрій чи браузер. Це просто член розподіленої системи, який може зазнати затримок у мережі та розділення мережі. (тобто ви використовуєте свій смартфон у метро)

Якщо ми можемо створити мережеві розділи та бази даних, що мають толерантність до швидкості мережі (я маю на увазі, що ми можемо виконувати операції запису на ізольований вузол), ми можемо, можливо, створити програмне забезпечення для фронтальних програм (мобільний або настільний), натхненні цими концепціями, які добре працюють з офлайн-режимом, який підтримується вікна без додатків, функцій програми немає.

Я думаю, що ми повинні наснажити себе тим, як бази даних працюють над архітектурою наших прикладних програм. Слід зауважити, що ці програми не виконують запити POST та PUT та GET ajax для надсилання даних один одному, а скоріше використовують журнали подій та CRDT для забезпечення можливої ​​послідовності.

То чому б цього не зробити на фронталі? Зауважте, що бекенд вже рухається в цьому напрямку, інструменти на зразок Кафки масово приймаються великими гравцями. Це так чи інакше пов'язане з подіями Sourcing / CQRS / DDD.

Перевірте ці чудові статті авторів Kafka, щоб переконати себе:

Можливо, ми можемо почати, надсилаючи команди на сервер та отримуючи потік подій сервера (через веб-розетки для зразка), замість того, щоб запускати запити Ajax.

Мені ніколи не було дуже комфортно з проханнями Ajax. Як ми реагуємо, розробники, як правило, є функціональними програмістами. Я думаю, що важко міркувати про місцеві дані, які повинні бути вашим "джерелом істини" вашого додатка для інтерфейсу, в той час як справжнє джерело істини фактично знаходиться на базі даних сервера, а ваше "локальне" джерело правди вже може бути застарілим коли ви отримаєте його, і ніколи не зійдеться на реальне джерело значення істини, якщо ви не натиснете якусь кульгаву кнопку Refresh ... Це інженерія?

Однак все-таки трохи важко спроектувати таку річ з явних причин:

  • Ваш клієнт мобільного зв’язку / браузера має обмежені ресурси, і він не може обов'язково зберігати всі дані локально (тому іноді потрібне опитування з великим вмістом запиту ajax)
  • Ваш клієнт не повинен бачити всі дані розподіленої системи, тому потрібно якось фільтрувати події, які він отримує з міркувань безпеки

3
Чи можете ви навести приклад використання Q обіцянок з діями?
Метт Фокс Дункан

@MattFoxxDuncan не впевнений, що це така гарна ідея, оскільки він робить "журнал подій" несеріалізаційним і робить оновлення магазину асинхронно під час запуску дій, тому він має деякі недоліки. Однак якщо це нормально для вашої справи, і ви розумієте ці недоліки, це досить зручно і зменшити котельну плиту. З Fluxxor, напевно, ви можете зробити щось на кшталтthis.dispatch("LOAD_DATA", {dataPromise: yourPromiseHere});
Себастьян Лорбер

Повністю не згоден з вашим аргументом AJAX. Насправді читати було дуже прикро. Ви читали свої зауваження? Подумайте про магазини, ігри, додатки, які заробляють серйозні гроші - всі вимагають дзвінків на сервер API та AJAX. Подивіться на Firebase, якщо ви хочете "без сервера" чи щось подібного, але AJAX тут, щоб сказати, що я сподіваюся, що принаймні ніхто з них не згоден Ваша логіка
TheBlackBenzKid

@TheBlackBenzKid Я не кажу, що Ajax повністю зникне за рік (і будьте впевнені, що я все ще будую веб-сайти на вершині запитів ajax, які зараз є СОТ стартапу), але я кажу, що це, швидше за все, зникне тому, що це не достатньо хороший протокол для обробки можливої ​​послідовності, яка вимагає потокової передачі, а не опитування, а можлива узгодженість - це те, що дозволяє додаткам надійно працювати в автономному режимі (так, ви можете зламати щось з локальним зберіганням самостійно, але у вас буде обмежений потенціал в режимі офлайн, або ваш додаток дуже простий). Проблема не в кешування, це недійсне кеш.
Себастьян Лорбер

@TheBlackBenzKid Моделі, що стоять за Firebase, Meteor тощо, недостатньо хороші. Чи знаєте ви, як ці системи обробляють одночасне записування? останнє записування-виграш замість причинно-наслідкових стратегій / злиття стратегій? Чи можете ви дозволити собі переважну роботу колеги в додатку, коли обидва працюють на ненадійних зв’язках? Також зауважте, що ці системи, як правило, поєднують багато локальних та серверних моделей. Чи знаєте ви будь-який добре відомий додаток для спільної роботи, який суттєво складний, ідеально працює в автономному режимі, заявляючи, що це задоволений користувач Firebase? Я не
Себастьян Лорбер

20

Ви можете зателефонувати за інформацією або в автори дій, або в магазини. Важливим є не обробляти відповідь безпосередньо, а створювати дію в зворотному / виклику помилок / успіх. Поводження з відповіддю безпосередньо в магазині призводить до більш крихкого дизайну.


9
Чи можете ви пояснити це більш детально? Скажіть, мені потрібно зробити початкове завантаження даних із сервера. У поданні контролера я запускаю дію INIT, і Store запускає ініціалізацію асинхронізації, що відображає цю дію. Тепер я б пішов з думкою, що коли Магазин отримує дані, він просто випромінює зміни, але не починає дії. Тож випромінення змін після ініціалізації повідомляє погляди про те, що вони можуть отримати дані з магазину. Чому після успішного завантаження не потрібно змінювати зміни, а починати іншу дію ?! Дякую
Jim-Y

Fisherwebdev, про магазини, що викликають дані, роблячи це, чи не ви порушуєте парадигму Flux, єдиний 2 належних способу, на які я можу подумати, щоб зателефонувати за допомогою даних, використовуючи: 1. використовувати клас завантаження, використовуючи дії для завантаження даних 2 Перегляди, знову використовуючи Дії для завантаження даних
Йотам

4
Виклик даних - це не те саме, що отримання даних. @ Jim-Y: ви повинні змінювати зміни лише після того, як дані в магазині фактично змінилися. Йотам: Ні, виклик даних у магазині не порушує парадигми. Дані слід отримувати лише через дії, щоб усі магазини могли бути поінформовані за будь-якими новими даними, що надходять у програму. Таким чином, ми можемо викликати дані в магазині, але коли відповідь повернеться, нам потрібно створити нову дію, а не обробляти її безпосередньо. Це забезпечує додаток гнучким та стійким до розробки нових функцій.
fisherwebdev

2

Я використовую приклад Бінарної Музи з прикладу Fjaxxor 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% синхронними.


2

Я відповів на відповідне запитання тут: Як обробляти вкладені дзвінки 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
  }
}

0

Ось я сприймаю це: http://www.thedreaming.org/2015/03/14/react-ajax/

Сподіваюся, що це допомагає. :)


8
знижуюче значення відповідно до правил. розміщення відповідей на зовнішніх сайтах робить цей сайт менш корисним, а також робить відповіді нижчої якості, знижуючи корисність сайту. зовнішні URL-адреси, ймовірно, також будуть вчасно зламані. downvote нічого не говорить про корисність статті, що, до речі, дуже добре :)
oligofren

2
Хороший пост, але додаючи короткий підсумок плюсів / мінусів кожного підходу, ви отримаєте корисні результати. Так, нам не потрібно клацати посилання, щоб отримати суть вашої відповіді.
Cory House
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.