Чи повинні магазини флюсу або дії (або обидва) торкатися зовнішніх послуг?


122

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

-OR-

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

Мені здається, що це має бути те чи інше (а не суміш обох). Якщо так, то чому один бажаний / рекомендований над іншим?


2
Ця публікація може допомогти code-experience.com/…
Markus-ipse

Для тих, хто оцінює різні реалізації схеми потоку, я дуже рекомендую поглянути на Redux github.com/rackt/redux Магазини реалізовані як чисті функції, що приймають у поточному стані та випромінюють нову версію цього стану. Оскільки вони суто функціонують, питання про те, чи можуть вони викликати мережеві та сервісні сховища, виходить з ваших рук: вони не можуть.
плаксдан

Відповіді:


151

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

  1. Ваші магазини стають повністю синхронними. Це робить логіку вашого магазину набагато простішим у дотриманні та дуже легко перевірити - просто інстанціюйте магазин із деяким заданим станом, надішліть йому дію та перевірте, чи змінився стан, як очікувалося. Крім того, однією з основних концепцій потоку є запобігання каскадних відправлень та попередження декількох відправлень одночасно; це дуже важко зробити, коли ваші магазини роблять асинхронну обробку.

  2. Усі розсилки дій відбуваються від творців дій. Якщо ви обробляєте асинхронні операції у ваших магазинах, і хочете підтримувати обробники дій ваших магазинів синхронними (і вам слід для того, щоб отримати гарантії одноразового відправлення), вашим магазинам потрібно буде запустити додаткові дії УСПІХУВАННЯ та НЕПРАВИ у відповідь на асинхронні дії обробка. Введення цих відправлень у творці дій замість цього допомагає розділити завдання творців та магазини; Крім того, вам не доведеться копати логіку магазину, щоб зрозуміти, звідки надсилаються дії. Типова асинхронна дія в цьому випадку може виглядати приблизно так (змінити синтаксис dispatchвикликів на основі аромату потоку, який ви використовуєте):

    someActionCreator: function(userId) {
      // Dispatch an action now so that stores that want
      // to optimistically update their state can do so.
      dispatch("SOME_ACTION", {userId: userId});
    
      // This example uses promises, but you can use Node-style
      // callbacks or whatever you want for error handling.
      SomeDataAccessLayer.doSomething(userId)
      .then(function(newData) {
        // Stores that optimistically updated may not do anything
        // with a "SUCCESS" action, but you might e.g. stop showing
        // a loading indicator, etc.
        dispatch("SOME_ACTION_SUCCESS", {userId: userId, newData: newData});
      }, function(error) {
        // Stores can roll back by watching for the error case.
        dispatch("SOME_ACTION_FAIL", {userId: userId, error: error});
      });
    }
    

    Логіку, яка в іншому випадку може дублюватися через різні дії, слід витягувати в окремий модуль; у цьому прикладі був би той модуль SomeDataAccessLayer, який обробляє виконання фактичного запиту Ajax.

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


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

1
Чи було б краще / легше перевірити фактичну логіку запиту в модулі API? Таким чином, ваш модуль API може просто повернути обіцянку, з якої ви відправляєте. Творець дії просто розсилає на основі вирішення / відмови після надсилання початкової дії "в очікуванні". Залишається питання, як компонент слухає ці «події», оскільки я не впевнений, що стан запиту повинен відображати стан для зберігання стану.
бекдек

@backdesk Це саме те, що я роблю у наведеному вище прикладі: відправте початкову дію, що очікує на розгляд ( "SOME_ACTION"), використовуйте API, щоб зробити запит ( SomeDataAccessLayer.doSomething(userId)), який повертає обіцянку, і в двох .thenфункціях відправити додаткові дії. Стан запиту може (більше чи менше) відображати карту для зберігання стану, якщо додатку потрібно знати про стан держави. Як ця карта залежить від програми (наприклад, може бути, кожен коментар має індивідуальний стан помилок, a la Facebook або, можливо, є один компонент глобальної помилки)
Мішель Тіллі,

@MichelleTilley "Однією з основних концепцій потоку є запобігання каскадних відправлень та попередження декількох розсилок одночасно; це дуже важко зробити, коли ваші магазини роблять асинхронну обробку". Це для мене ключовий момент. Добре сказано.

51

Я написав це питання дияволам у Facebook, і я отримав відповідь від Білла Фішера:

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

Але коли у вас є тикер або інший нелюдський водій, дзвінок з магазину працює краще.

Важливо - створити дію в зворотному / виклику помилок / успіх, тому дані завжди походять із дій


Хоча це має сенс, будь-яка ідея чому a call from store works better when action triggers from non-human driver ?
SharpCoder

@SharpCoder Я думаю, якщо у вас є живий тикер або щось подібне, вам дійсно не потрібно активувати дію, і коли ви робите це з магазину, вам, ймовірно, доведеться написати менше коду, оскільки магазин може миттєво отримати доступ до штату & випромінюють зміни.
Флоріан Вендельборн

8

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

У кожному застосуванні Flux, що я бачив, дії - це в основному рядки подій, перетворені на об'єкти, як, наприклад, традиційно у вас буде подія під назвою "прив’язка: натиснута", але в Flux вона буде визначена як AnchorActions.Clicked. Вони навіть настільки "німі", що більшість реалізацій мають окремі об'єкти Dispatcher, щоб фактично відправляти події до магазинів, які слухають.

Особисто мені подобається реалізація Flux від Reflux, коли немає окремих диспетчерських об'єктів, а об'єкти Action виконують диспетчеризацію самостійно.


редагувати: Flux Facebook фактично потрапляє до "творців дій", тому вони використовують розумні дії. Вони також готують корисне навантаження, використовуючи магазини:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/action/ChatMessageActionCreators.js#L27 (рядок 27 та 28)

Після цього зворотний виклик після завершення може викликати нову дію цього разу із отриманими даними як корисне навантаження:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/utils/ChatWebAPIUtils.js#L51

Тож я гадаю, що це краще рішення.


Що це за реалізація Reflux? Я не чув про це. Ваша відповідь цікава. Ви маєте на увазі, що ваша реалізація магазину повинна мати логіку здійснювати дзвінки API тощо? Я подумав, що магазини повинні просто отримувати дані та просто оновлювати їх значення. Вони фільтрують конкретні дії та оновлюють деякі атрибути своїх магазинів.
Джеремі D

Reflux - це незначна зміна потоку Facebook у Flux: github.com/spoike/refluxjs Магазини керують усім доменом "Модель" вашої програми, порівняно з Action / Dispatchers, який лише з'єднує та склеює речі.
Ригу

1
Тож я ще раз про це думав і (майже) відповів на власне питання. Я би додав це як відповідь тут (щоб інші голосували), але, мабуть, я занадто бідний на карму в стаковому потоці, щоб можна було ще відповісти. Тож ось посилання: groups.google.com/d/msg/reactjs/PpsvVPvhBbc/BZoG-bFeOwoJ
plaxdan

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

Я редагував свою відповідь альтернативним виглядом. Здається, обидва рішення можливі. Я б майже напевно вибрав рішення Facebook над іншими.
Ригу

3

Я надам аргумент на користь "німих" дій.

Покладаючи відповідальність за збір даних перегляду у своїх діях, ви приєднуєте свої дії до вимог даних щодо ваших поглядів.

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

Це піддається більш численним, але меншим, більш спеціалізованим магазинам. Я аргументую цей стиль тому, що

  • це дає вам більше гнучкості в тому, як представлення споживають дані магазину
  • "розумні" магазини, спеціалізовані для представлень, які споживають їх, будуть меншими та менш зв'язаними для складних додатків, ніж "розумні" дії, від яких залежить багато переглядів

Метою магазину є надання даних переглядам. Назва "Дія" підказує мені, що її мета - описати зміну моєї Заявки.

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

За допомогою "розумних" дій вам може знадобитися змінити дію "оновити інформаційну панель", щоб використовувати новий API. Однак "Оновлення інформаційної панелі" в абстрактному сенсі не змінилося. Вимоги до даних ваших поглядів - це те, що змінилося.

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


2

Демонстратор маршрутизатора gaeron- flux- react -демонструє корисну варіацію «правильного» підходу.

ActionCreator генерує обіцянку від зовнішньої служби API, а потім передає обіцянку та три константи дії dispatchAsyncфункції у проксі / розширеному диспетчері. dispatchAsyncзавжди передаватиме першу дію, наприклад, "GET_EXTERNAL_DATA", і коли обіцянка повернеться, вона буде відправлена ​​або "GET_EXTERNAL_DATA_SUCCESS", або "GET_EXTERNAL_DATA_ERROR".


1

Якщо ви хочете, щоб в один день було середовище розробки, порівнянне з тим, що ви бачите у відомому відео Брет Віктора « Винахід на принцип» , скоріше скористайтеся тупими сховищами, які є лише проекцією дій / подій всередині структури даних без жодних побічних ефектів. Це також допоможе, якби ваші магазини насправді були членами тієї самої глобальної незмінної структури даних, як у Redux .

Більше пояснень тут: https://stackoverflow.com/a/31388262/82609

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.