Как стать автором
Обновить

Комментарии 36

теперь при помощи count.increment мы можем создать генератор действий напрямую из редьюсера.


Идея прямо скажем противоположная тому что предлагает redux. Скорее всего даже вредная.

На мой взгляд основная проблема redux имеет в основе основную проблему react а именно синхронность. В результате кому не знакома такая ситуация. Мы рендерим компонент. При этом в событии жизненного цикла запрашиваем данные с сервера. Далее компонент рендерится с пустым стором (для того чтобы это не вылетало в ошибку нужно дополнительно всяких условий накидать или синхронно инициализировать стор пустыми значениями. Если кто как делает по-другому то прошу поделиться опытом). Потом по получении данных с сервера происходит повторный рендер.

Я честно говоря нашел один хук куда внедрил асинхронность — это как ни странно в компонент Link. Это избавило меня от необходимости в этих неестественных проверках стора на непустоту и псевдо-инициализаций стора, а пользователя в наблюдении прелоадеров. См. например вариант приложения real-world realworld-react-universal-hot-iltreezyct.now.sh/author/Apa%20Pacy

Но в этом году все должно измениться. См. доклад Дэна Абрамова habrahabr.ru/post/350816 с русскими субтитрами. Вобщем-то значение redux (в современном виде) после этого немного становится туманным.
Идея прямо скажем противоположная тому что предлагает redux. Скорее всего даже вредная.

Мне вот всегда было интересно. Допустим, можно реально генерировать AC из редюсеров (если нам правда хватает этой информации в редюсерах), то в чем вредность такой генерации? Зачем писать дополнительный код, который пишется копипастой, если уже написано достаточно кода и на его базе можно сгенерировать всё необходимое? Так почему писать меньше ненужного кода — вредно?
Не понял что такое АС если можно расшифруйте.

Генерировать «действия» в редьюсере мне кажется нелогичным.
Давайте посмотрим что из себя представляет редьюсер. Я честно говоря не в восторге от терминологии redux возможно в этом причина, что многим эта технология не по душе. (Мне тоже кстати была пока я не почитал курс Максима Пацианского см. habrahabr.ru/post/351046 )

Редьюсер это собственно функция которая вызывается при выполнении каждого действия.
Она может вернуть тот же самый объект и тогда ни один компонент не будет перерисован. Либо может вернуть новый объект. Тогда все компоненты которые слушают изменение состояния будут обновлены. Таких редьюсеров обычно несколько в одном приложении.

Предназначение и функция редьюсера это просто вернуть старое или обновленное состояние объекта. Вызов каждого действия вызывает все функции-редьюсеры но «срабатывает» только нужная из них. При этом гарантируется что сналачало инициируется действие. Потом вызываются редбюсеры. Потом опять действие. потом опять редьюсеры и т.д. То есть образуется своеобразная цепь переходов их одного состояние в другое.

Что если из редьюсера начать вызывать действия? Синхронные или асинхронные не так уж важно. Это же еще не пробежалась система по всем редьюсерам. Это все компоненты которые слушают редьюсеры не обработали обновленное состояние стора. И уже пошел вызов нового действия. Фактически мы входим в рекурсию при этом. И все может стать настолько заполненным действиями, что до рендеринга обновленного состояния стора и дело не дойдет.
Не понял что такое АС если можно расшифруйте.

ActionCreator

Что если из редьюсера начать вызывать действия? [...] Фактически мы входим в рекурсию при этом

Простите, не путайте «вызывать действия из редьюсера» и «генерировать ActionCreator на базе редьюсера». Так как вы спутали эти две вещи — остальные ваши слова не имеют смысла. Вот у вас есть типичный экшн:
function apologize(fromPerson, toPerson, error) {
  return {
    type: 'APOLOGIZE',
    fromPerson,
    toPerson,
    error
  };
}


Какой в нем смысл? Такие функции должны стать рутинной работой генератора, а не копипаститься.
Ну как правило действия все же более развернутые. У меня таких практически (то есть совсем) не встречается. Хотя я видел у некоторых и для скорости они прямо диспатчили объектами это — не функциями так примерно
dispatch({
type: 'APOLOGIZE',
fromPerson,
toPerson,
error
};)

А какие у вас? Покажите пример

Можно бесконечно смотреть на три вещи: как горит огонь, как течет вода, и как кто-то совершенствует Redux.
Хорошая попытка уйти от тонны копипасты, хотя она явно противоречит идеологии автора Редакса.

Один вопрос. Что делать, если, скажем, в эффекте необходимо менять не один стор (не знаю, как еще такой блок назвать), а несколько?

Кстати, я вот подумал. Можно пойти дальше и убрать редюсеры (они ведь тут остались как ФП ради ФП), изменять стейт в effects, а так как они остались одни — вынести на уровень выше. Получится, внезапно, MobX!

export class Count {
  state: 0, // initial state

  @action increment (delta) {
    this.state += delta
  }

  async incrementAsync(delta) {
    await new Promise(resolve => setTimeout(resolve, 1000))
    this.increment(delta);
  }
}
Насчет копипасты можно поподробнее? Что и куда копипастится в redux? Я просто обычно не делаю этого. Поэтому спрашиваю.
Когда вы создаете самый просто action — сколько раз вы нажимаете ctrl+c/ctrl+v? Я чуть выше привел пример действия 'APOLOGIZE'

Условно:


const CALC_ACTION = 'CALC_ACTION';

const calcActionCreator = (a, b, c) => { type: CALC_ACTION, payload :{a, b, c }};

const calc = (arg, a, b, c) => { x: arg.x +a, y: arg.y * b, z: c}

const INITIAL_STATE = {x: 3, y: 4: z: 5 };

const reducer = (state = INITIAL_STATE, action) => {
// ...
  case SOME_ACTION:
    return {...state, ...calc(state, ...action.payload))};
// ...
};

dispatch(calcActionCreator({a: 6, b: 9, c: 10});

Как по мне, то достаточно много копипасты, и это всё в одном файле, без импортов — с ними ещё больше.

Уровень наркомании не достаточен.
Например redux-restate может взять два стора на вход, и выдать один стор на выход (только для react «части»), а redux-loop может по некому redux action вызвать что-то из context текущего компонента, что может в итоге вызвать dispatch в сторе родительсткого приложения.

Мы сейчас используем и первое и второе чтобы как-то связать вроде бы два независимых приложения с независимыми сторами, вложенные одно в другое.
Не достаточен кому? В MobX как бы проблемы «соединения сторов» нет by design за отсутствием этих самых сторов.
Счастливые вы люди.

Каждый раз когда кто-то начинает говорить о недостатках redux, у меня возникает вопрос — почему не использовать уже mobx, черт возьми.

Потому что у большинства молодых разработчиков redux головного мозга. Мы когда искали фронтенд-джунов. Разбирали буквально несколько десятков тестовых заданий. На 25 заданий, только один использовал «не Redux». Когда спрашивал «почему?», говорят «посмотрите на вакансии на рынке — везде Redux». При этом у нас стоит условие «dataflow» на ваше усмотрение.

Мы занимаемся разработкой и поддержкой крупных проектов. В своё время в ужасе убежали с Redux на MobX + наша библиотека github.com/wearevolt/mobx-model которая позволяет держать в памяти модели-сторы с перекрёстными связями. Это позволяет легко загружать и обрабатывать сложные графы десятков видов моделей, и при этом не сходить с ума.
Забавно, я в своем профессиональном развитии как раз перешел на redux чтобы не держать в памяти модели-сторы с перекрестными связями и не сходить от этого с ума. Ибо до этого было что-то похожее, но постарее (JSData) — так вот там как раз огреб весь кошмар от «это computable проперти зависит от этой модели и от этой, а чтобы их загрузить надо знать ещё эту модель. и когда мы обновляем что-то, у нас идет пересчет кучи моделей.»

Прелесть MobX, что не надо знать. граф зависимостей под капотом вычисляется и перевычисляется автоматически.

Вроде как нигде не нужно знать, и везде всё вычисляется автоматически. Вопросы возникают когда "что-то пошло не так", стектрейс прямиком из ада, в проекте цепочки синхронно-асинхронных-отложенных зависимых computed-значений (этакая матрёшка в много разнородных слоёв). Последовательность вычислений начинает сильно зависеть от конкретных кликов мыши и др. нюансов. И в итоге клубок собирается по-разному. И даже воспроизведя проблему ты не знаешь, как это повторить заново. У меня был такой опыт (KnockoutJS), неоднократно.

Простите, а вы пользовались МобХ? Я просто пользовался довольно много, написал уже не один проект (и довольно сложные) и никогда не стыкался с такими проблемами

Нет, только KnockoutJS. MobX и Vue у меня первые в списке на пробу, когда подвернётся возможность. Очень уж много про них хорошего говорят.

Вот я про это же. Причем в обычных веб приложениях этого можно избежать если вся система достаточно простая, но если прям «сложные графы десятков видов моделей»…

Я пытаясь уйти от копипасты пришёл к такой схеме:


const A = actionFactory('PREFIX');

export const aExpandTreeItem = A.create('EXPAND', 'id');
export const aSomeAnotherAction = A.create('SOME', 'arg1, arg2, arg3');
// === (arg1, arg2, arg3) => ({ type: `PREFIX_SOME`, arg1, arg2, arg3 });

const map = 
{
  [aExpandTreeItem]: (state, { id }) => { /* ... */ },
  [aSomeAnotherAction]: (state, { arg1, arg2, arg3 }) => { /* ... */ },
};

Но дубляжа осталось всё равно много, просто он стал компактнее. Дублируются поля в action-ах дважды, дублируются имена action-ов четырежды (export, import, A.create, map). Меньше помощи от IDE за счёт строк (правда мне ST3 итак не помогает)


Потенциально можно убрать actionCreator-ы совсем, генерируя их из map-ы. Но это терпит крах когда нам нужны actionCreator-ы, которые являются композицией над другими и когда нужны async-actionCreator-ы. Да и вообще не всегда хочется держать список доступных action-ов там же, где и их reducer.


Сложилось впечатление, что пока мы держим action-ы и reducer-ы отдельно, и хотим какой-то надёжности на уровне разруливания зависимостей в import-export — копипасты будет много.

Кстати — одно из самых главный ограничений «всех этих библиотек»(убийц редакса) — они делает вещи простыми, делают «стор» доступным из любого места, и все такое.
Круто и удобно для клиентсайда.
Круто и удобно для старого доброго синхронного серверсайда.
Вообще никак не работает с новым кленовым асинхронным SSR. Потому что стор не паралелиться.
Выход тут только один, и многие по нему идут – один рендер == 1 процесс.

Прощай nodejs, привет Apache prefork!
Раскройте, пожалуйста, что именно в данном случае приводит к тому, что стор «не параллелится», а то непонятно как-то.
Я вообще не понял начиная с «стор не паралелиться». Что это значит? И вот это: «один рендер == 1 процесс». В JS впринципе один процесс, я редко видел, чтобы кто-то использовал ВебВоркеры.
На сервере может выполнять множество рендеров одновременно. Кто-то все еще считает, что Js это только фронтенд, 1 процесс и ровно 1 задача на исполнение — и из-за этого этот самый Js все еще именно такой.
В случае с реактом есть 3 варианта рендеринга на сервер сайде:
— старый добрый MVC, где все будет готово до «реакта», он сам будет renderToString
— микс, где мы используем Реакт чтобы инициализировать данные, а потом renderToString
— «новый», с renderToStream и «suspence» под капотом.
Асинхронность на сервер-сайде, и использованием lifecycle Реакта — нычне стало легко и удобно. Но любой глобал это дело пресекает на корню.
Асинхронность на сервер-сайде, и использованием lifecycle Реакта — нычне стало легко и удобно. Но любой глобал это дело пресекает на корню.

Почему пресекает?

Потому что две разные страницы будут шарить один глобальных «store», в то время как должны быть полностью изолированы друг от друга.
«Обычный» redux поддерживает это из коробки. Rematch, react-copy-write и другие — этот принцип ломают.
Потому что две разные страницы будут шарить один глобальных «store», в то время как должны быть полностью изолированы друг от друга.

Так смысл редакса и состоит в том, чтобы был один глобальный стор, который шарится. Как вы в редаксе обеспечите изоляцию, если отсутствие расшаренного стора = не-редакс?


И в чем таки проблемы с асинхронностью при наличии глобального расшаренного стора? Смысл асинхронности же как раз и состоит в том, что ей глобальные объекты не могут мешать.

Тот «стор» с которым работать можно, лежит в контексте и для каждого инстанса свой. Только протяни руки в контекст и возьми.
Еще есть 100500 различных redux обвязок, которые берут этот «стор», что-то с ним делают, и кладут обратно. Простейщий пример — habr.com/post/346116, и таких десятки, на все-все случаи в жизни.
Тот «стор» с которым работать можно, лежит в контексте и для каждого инстанса свой.

То есть, это уже не редакс? И как вы решаете задачи синхронизации разных сторов? Их же надо в итоге потом в один свести, а там будут противоречивые варианты одних и тех же данных. Как выбираете корректные данные?
И так и непонятно в чем проблемы с асинхронностью-то. Ну есть у вас один глобальный стор, и?

Проблема про сервер сайд — _Одновременно_ генерятся страницы для _разных_ клиентов.
Проблема про сервер сайд — Одновременно генерятся страницы для разных клиентов.

И в чем проблема с асинхронностью-то?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории