Redux. Простой как грабли

    Мне уже доводилось заглядывать в репозиторий библиотеки redux, но откуда-то появилась мысль углубиться в его реализацию. Своим в некотором роде шокирующим или даже разочаровывающим открытием я хотел бы поделиться с сообществом.

    TL;DR: базовая логика redux помещается в 7 строк JS кода.

    О redux вкратце (вольный перевод заголовка на гитхабе):
    Redux — библиотека управления состоянием для приложений, написанных на JavaScript.

    Она помогает писать приложения, которые ведут себя стабильно/предсказуемо, работают на разных окружениях (клиент/сервер/нативный код) и легко тестируемы.
    Я склонировал репозиторий redux, открыл в редакторе папку с исходниками (игнорируя docs, examples и прочее) и взялся за ножницы клавишу Delete:

    • Удалил все комментарии из кода
      Каждый метод библиотеки задокументирован с помощью JSDoc весьма подробно
    • Убрал валидацию и логирование ошибок
      В каждом методе жёстко контролируются входные параметры с выведением очень приятных глазу подробных комментариев в консоль
    • Убрал методы bindActionCreators, subscribe, replaceReducer и observable.

      … потому что мог. Ну или потому что поленился писать для них примеры. Но без корнер-кейсов они ещё менее интересны, чем то, что ждёт вас впереди.

    А теперь давайте разберём то, что осталось



    Пишем redux за 7 строк


    Весь базовый функционал redux умещается в малюсенький файлик, ради которого вряд ли кто-нибудь будет создавать github репозиторий :)

    function createStore(reducer, initialState) {
        let state = initialState
        return {
            dispatch: action => { state = reducer(state, action) },
            getState: () => state,
        }
    }
    

    Всё. Да, серьёзно, ВСЁ.

    Так устроен redux. 18 страниц вакансий на HeadHunter с поисковым запросом «redux» — люди, которые надеются, что вы разберетесь в 7 строках кода. Всё остальное — синтаксический сахар.

    С этими 7 строками уже можно писать TodoApp. Или что угодно. Но мы быстренько перепишем TodoApp из документации к redux.

    // Инициализация хранилища
    function todosReducer(state, action) {
      switch (action.type) {
        case 'ADD_TODO':
          return [
            ...state,
            {
              id: action.id,
              text: action.text,
              completed: false
            }
          ]
        case 'TOGGLE_TODO':
          return state.map(todo => {
            if (todo.id === action.id) {
              return { ...todo, completed: !todo.completed }
            }
            return todo
          })
        default:
          return state
      }
    }
    
    const initialTodos = []
    
    const store = createStore(todosReducer, initialTodos)
    
    // Использование
    store.dispatch({
      type: 'ADD_TODO',
      id: 1,
      text: 'Понять насколько redux прост'
    })
    
    store.getState() 
    // [{ id: 1, text: 'Понять насколько redux прост', completed: false }]
    
    store.dispatch({
      type: 'TOGGLE_TODO',
      id: 1
    })
    
    store.getState() 
    // [{ id: 1, text: 'Понять насколько redux прост', completed: true }]
    

    Уже на этом этапе я думал бросить микрофон со сцены и уйти, но show must go on.
    Давайте посмотрим, как устроен метод.

    combineReducers


    Это метод, который позволяет вместо того, чтобы создавать один огромный reducer для всего состояния приложения сразу, разбивать его на отдельные модули.

    Используется он так:

    // здесь мы переиспользуем метод todosReducer из прошлого примера
    
    function counterReducer(state, action) {
      if (action.type === 'ADD') {
        return state + 1
      } else {
        return state
      }
    }
    
    const reducer = combineReducers({
      todoState: todoReducer,
      counterState: counterReducer
    })
    
    const initialState = {
      todoState: [],
      counterState: 0,
    }
    
    const store = createStore(reducer, initialState)
    

    Дальше использовать этот store можно так же, как предыдущий.

    Разница моего примера и описанного в той же документации к TodoApp довольно забавная.

    В документации используют модный синтаксис из ES6 (7/8/∞):

    const reducer = combineReducers({ todos, counter })
    

    и соответственно переименовывают todoReducer в todos и counterReducer в counter. И многие в своём коде делают то же самое. В итоге разницы нет, но для человека, знакомящегося с redux, с первого раза эта штука выглядит магией, потому что ключ части состояния (state.todos) соответствует функции, названной также только по желанию разработчика (function todos(){}).

    Если бы нам нужно было написать такой функционал на нашем micro-redux, мы бы сделали так:

    function reducer(state, action) {
      return {
        todoState: todoReducer(state, action),
        counterState: counterReducer(state, action),
      }
    }
    

    Этот код плохо масштабируется. Если у нас 2 «под-состояния», нам нужно дважды написать (state, action), а хорошие программисты так не делают, правда?
    В следующем примере от вас ожидается, что вы не испугаетесь метода Object.entries и Деструктуризации параметров функции
    Однако реализация метода combineReducers довольно простая (напоминаю, это если убрать валидацию и вывод ошибок) и самую малость отрефакторить на свой вкус:

    function combineReducers(reducersMap) {
      return function combinationReducer(state, action) {
        const nextState = {}
        Object.entries(reducersMap).forEach(([key, reducer]) => {
          nextState[key] = reducer(state[key], action)
        })
        return nextState
      }
    }
    

    Мы добавили к нашему детёнышу redux ещё 9 строк и массу удобства.

    Перейдём к ещё одной важной фиче, которая кажется слишком сложной, чтобы пройти мимо неё.

    applyMiddleware


    middleware в разрезе redux — это какая-то штука, которая слушает все dispatch и при определенных условиях делает что-то. Логирует, проигрывает звуки, делает запросы к серверу,… — что-то.

    В оригинальном коде middleware передаются как дополнительные параметры в createStore, но если не жалеть лишнюю строчку кода, то использование этого функционала выглядит так:

    const createStoreWithMiddleware = applyMiddleware(someMiddleware)(createStore)
    const store = createStoreWithMiddleware(reducer, initialState)
    

    При этом реализация метода applyMiddleware, когда ты потратишь 10 минут на ковыряние в чужом коде, сводится к очень простой вещи: createStore возвращает объект с полем «dispatch». dispatch, как мы помним (не помним) из первого листинга кода, — это функция, которая всего лишь применяет редюсер к нашему текущему состоянию (newState = reducer(state, action)).
    Так вот applyMiddleware не более чем переопределяет метод dispatch, добавляя перед (или после) обновлением состояния какую-то пользовательскую логику.

    Возьмём, например, самый популярный middleware от создателей redux — redux-thunk

    Его смысл сводится к тому, что можно делать не только

    store.dispatch({type: 'SOME_ACTION_TYPE', some_useful_data: 1 })

    но и передавать в store.dispatch сложные функции

    function someStrangeAction() {
      return async function(dispatch, getState) {
        if(getState().counterState % 2) {
           dispatch({
             type: 'ADD',
           })
        }
        await new Promise(resolve => setTimeout(resolve, 1000))
        dispatch({
          type: 'TOGGLE_TODO',
          id: 1
        })
      }
    }
    

    И теперь, когда мы выполним команду

    dispatch(someStrangeAction())
    

    то:

    • если значение store.getState().counterState не делится на 2, оно увеличится на 1
    • через секунду после вызова нашего метода, todo с id=1 переключит completed true на false или наоборот.

    Итак, я залез в репозиторий redux-thunk, и сделал то же самое что и с redux — удалил комментарии и параметры, которые расширяют базовый функционал, но не изменяют основной

    Получилось следующее:

    const thunk = store => dispatch => action => {
      if (typeof action === 'function') {
        return action(store.dispatch, store.getState)
      }
      return dispatch(action)
    }
    

    я понимаю, что конструкция
    const thunk = store => dispatch => action
    выглядит жутковато, но её тоже просто нужно вызвать пару раз с произвольными параметрами и вы осознаете, что всё не так страшно, это просто функция, возвращающая функцию, возвращающую функцию (ладно, согласен, страшно)

    Напомню, оригинальный метод createStore выглядел так

    function createStore(reducer, initialState) {
        let state = initialState
        return {
            dispatch: action => { state = reducer(state, action) },
            getState: () => state,
        }
    }
    

    То есть он принимал атрибуты (reducer, initialState) и возвращал объект с ключами { dispatch, getState }.

    Оказалось, что реализовать метод applyMiddleware проще, чем понять, как он работает.
    Мы берём уже реализованный метод createStore и переопределяем его возвращаемое значение:

    function applyMiddleware(middleware) {
      return function createStoreWithMiddleware(createStore) {
        return (reducer, state) => {
          const store = createStore(reducer, state)
    
          return {
            dispatch: action => middleware(store)(store.dispatch)(action),
            getState: store.getState,
          }
        }
      }
    }

    Вывод


    Под капотом redux содержатся очень простые логические операции. Операции на уровне «Если бензин в цилиндре загорается, давление увеличивается». А вот то, сможете ли вы построить на этих понятиях болид Формулы 1 — уже решайте сами.

    P.S.


    Для добавления в мой «micro-redux» упрощённого метода store.subscribe потребовалось 8 строк кода. А вам?
    Поделиться публикацией

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

      +4

      Ваша реализация combineReducers нарушает главный инвариант redux "ссылочная эквивалентность равносильна отсутствию изменений". Этот инвариант важен, к примеру, для оптимизации рендера.


      Например, составной редьюсер должен выглядеть вот так:


      function reducer(state, action) {
        const next = {
          todoState: todoReducer(state.todoState, action),
          counterState: counterReducer(state.counterState, action),
        }
        if (next.todoState === state.todoState && next.counterState === state.counterState)
          return state;
        else
          return next;
      }
        0
        Я не работал с redux и react, но разве там нет селекторов с мемоизацией или подобия RxJS оператора distinctUntilChanged?
          +1
          Подобие distinctUntilChanged есть в React, называется PureComponent. Но чтобы оно работало — нужно избегать лишних пересозданий объекта, чего код автора не делает.
          +2

          Этот момент я и правда упустил. Но мой пример настолько вырожденный, что, надеюсь, читатели мне его простят :)

            +4
            Тем не менее, к вашей реализации combineReducers придётся добавить еще 3-5 строк. Учитывая общее количество строк, это важно.
          +4
          2019 год, несмотря на опыт индустрии, в попытках понять редакс пришлось проинспектировать весь его код, что бы написать его сломанную копию. Простите, но это забавно.

          Вообще редакс простой да, мне больше всего понравилось его объяснение как «EE с мидлварами и единственным атомом на конце». Проблема в том что современные джуны не знают ни про мидлвары, ни про EE (event emmiting), ни про атомы.

          P.S. в свое время это сделало мой день github.com/reduxjs/redux/commit/9276ff0af6400633d6731b15fed6e70c3561887e
            +2
            Нужно было так написать: Redux itself is very simple until it's not.
              +9
              У меня другой вопрос — а зачем вообще нужен редакс? Действительно ли нужны иммьютабельные состояния, чтобы отреагировать на нажатие кнопки? Несколько раз пытался изучить и внедрить его и каждый раз больше проблем, чем пользы.
              Сейчас используем vue и наконец-то разработка похожа на работу c UI, а не запуск космического корабля.
                +1
                Не любой инструмент применим к любой задаче. Конечно редакс не нужен для реакции на нажатие кнопки. Редакс, как single store, особенно удобен в монолитных приложениях, где есть сложная логика связанных данных и особенно важно иметь SSOT.

                Лично мое мнение, редакс — это фреймворк для реализации flux паттерна.
                  0
                  Ну тут сложилась странная ситуация: на голом react много не напишешь, разработчик react не дает никакого фреймворка, а только концепцию flux. Redux самая популярная реализация flux — при этом довольно костыльная как по мне: например ajax запросы очень странно делаются, заморочки с иммьютабельностью. И может я что-то не понимаю, но мне сложно писать на нем даже простые приложения, как на нем делают сложные вообще не представляю.
                    0
                    Архитектура редакса очень хороша и к ней стоит прислушиваться Вот проблема в бойлерплейте, это да.
                      0
                      1200 строк кода чтобы сказать «store → дай мне user'a → нету? → сходи на бэк»
                        +4
                        Не понимаю о каких 1200 строках речь
                        +1
                        При использовании хорошо подобранных интсрументов бойлерплейт вполне приемлемый.
                        +1
                        Как раз простые на нем писать не надо. Он оправдан когда количество компонент зашкаливает, скажем, за 100.
                          +4
                          Как раз простые на нем писать не надо. Он оправдан когда количество компонент зашкаливает, скажем, за 100.

                          Как раз нет, с ростом приложения польза от чистого редакса резко падает, и он начинает сильно мешать. Редакс не предоставляет ни средств для изоляции стейта и логики компонент, ни средств для абстрагирования, ни помощи для фиксации какой-то архитектуры.


                          Кроме того — искаробки не идет никаких методов для управления эффектами и отдельно — запросами. Стандартный подход (по три экшена на запрос) — не масштабируется от слова совсем, т.к. уже в среднего размера приложениях количество этих экшенов начинает измеряться тысячами, а любая нетривиальная логика становится очень сложной для отслеживания — во-первых она размывается по многим местам, во-вторых — ее отслеживание не поддерживается на уровне ide (в отличии от обычных функциональных вызовов).
                          кроме того — экшоны не composable.

                            +2
                            количество этих экшенов начинает измеряться тысячами

                            Мы уже очень долго обсуждали это ранее на хабре (это невозможно забыть) :-)


                            Это проблема именно вашего подхода к redux (а точнее к actionCreator-ам). Там где у всех 10 универсальных экшнов, у вас целая 1000 копипастных с бизнес-логикой.

                              0
                              Это проблема именно вашего подхода к redux (а точнее к actionCreator-ам). Там где у всех 10 универсальных экшнов

                              Я же специально указал, что это про "чистый редакс" и "Стандартный подход (по три экшена на запрос)".


                              Если же использовать редакс в качестве набора примитивов для построения своей системы стейт-менеджмента (где будет и изоляция, и архитектура, и абстракции и нормальная работа с асинхронщиной), то, конечно, это решаемо.


                              Ну и да, судя по гитхабам (да и по тому что я видел в своей практике) — как раз все и плодят эти десятки экшенов, которые превращаются в сотни (и, в перспективе — в тысячи), а исключение скорее как раз, когда над редаксом делают свою кастомную надстройку с генераторами экшенов/редьюсеров/саг и т.д.

                                0
                                Это и есть набор примитивов. О чем и статья.
                                  0
                                  Это и есть набор примитивов.

                                  Именно про это я и говорил. К сожалению, большинство этого не понимает и использует этот набор примитивов как есть.


                                  О чем и статья.

                                  Нет, статья совсем не о том.

                                    0
                                    =К сожалению, большинство этого не понимает и использует этот набор примитивов как есть.= Может быть потому что большинству просто не надо его использовать, как его нет? Я имею в виду нет необходимости чтото выдумывать, потому что он действительно простой как пробка.
                                      0
                                      Может быть потому что большинству просто не надо его использовать, как его нет?

                                      Такие варианты тоже есть. Но мы все же говорим не о микро-проектах. Там обычно редакс (или какой-то аналог) нужон.

                                        0
                                        oasisdigital.com/class/ngrx?gclid=Cj0KCQiAtP_iBRDGARIsAEWJA8gRtxn6oo-z1YBA6vkJCz1KD4jnoBba34P_sttXJ8AjyL0rbKE8lHYaAt-JEALw_wcB Там пониже есть список клиентов, которые используют редакс с ангулар. Мне лень искать чтото еще про реакт. Я не знаю в какой вселенной вы живете, пишите на чистых функциях большие проекты, видимо, но похоже мы не пересекаемся.
                                          0
                                          Там пониже есть список клиентов, которые используют редакс с ангулар.

                                          Я сам использовал (и продолджаю использовать) редакс с ангуляром.


                                          Я не знаю в какой вселенной вы живете, пишите на чистых функциях большие проекты

                                          Кто говорил про большие проекты на чистых функциях? Я говорил как раз про редакс (или аналог) на больших проектах. Вы невнимательно читаете.

                                0
                                А можно ссылку на дискуссию, если не затруднит? Благодарю заранее.
                                  0

                                  Нашёл. Забавно, но разговор начался с фразы "Просто скажите — сколько у вас тысяч экшенов?" :)

                                    +1
                                    Спасибо, все ясно )))
                                0
                                А что предоставляет?
                                  0

                                  Обычные функции, представьте себе. Они и composable, и нормально ищутся по коду, имеют нативные средства для группировки/изоляции и вообще имеют полную поддержку со стороны всей языковой инфраструктуры напрямую.


                                  Вообще, при работе с редаксом следует заменить с самого начала одну главную вещь — если вы создали action, то вы потом его диспатчите в стор. Ни для чего другого action'ы не используются, нет других сценариев. С-но, вместо того, чтобы иметь ф-и, которые создают экшены, вы можете создавать ф-и, которые создают экшон и сразу его диспатчат в стор. И потом работать с этими ф-ми вместо экшенов и экшен криэйторов. Это сразу все упрощает и делает на порядок удобнее.

                                    +1
                                    Да я уже понял, пишите на обычных функциях свой велосипед каждый раз, если никто не мешает, сочиняйте каждый раз свою архитектуру, абстракции и изоляции, а потом обучайте свою команду ему, раз уж редаксом пользоватья не получается. Вопрос же в том что у вас не получается, это не значит что редакс не подходит для больших проектов. Люди используют его во всему миру, и мне проще человеку дать ссылку почитать и он начнет писать редусеры — эпики — саги. Да, не идеально, но ничего другого вы лично кроме ванилла жс предложить не можете. Ну и потом, кто вообще вас заставляет пользоваться экшен криейторами, сделайте фактори и диспатчте на здоровье один акшен и свои фунции в пейлоаде, если так хочется, в чем проблема? Пара движений мышью и «главная вещь» ушла. Экшен криеторы это не редакс, никто не заставляет именно так создавать и диспетчить.
                                      +1
                                      Вопрос же в том что у вас не получается, это не значит что редакс не подходит для больших проектов.

                                      Я вроде уже говорил — он подходит для больших проектов, но не в чистом виде. Потому что в чистом виде — у вас тысячи экшенов, хаотично разбросанных, с размазанной логикой.


                                      редусеры — эпики — саги.

                                      Саги не имеют ничего общего с редаксом, там код совершенно независимый. Можно подключить саги к любой другой системе контроля эффектов.


                                      Ну и потом, кто вообще вас заставляет пользоваться экшен криейторами, сделайте фактори и диспатчте на здоровье один акшен и свои фунции в пейлоаде, если так хочется, в чем проблема?

                                      Так я же про это и говорю — не использовать чистый редакс, а использовать свои абстракции поверх него. С чем вы спорите?


                                      Экшен криеторы это не редакс, никто не заставляет именно так создавать и диспетчить.

                                      Это стандартные соглашения работы с редаксом. Если вы не используете экшон криейторы — значит, у вас своя обертка над редаксом. О чем я и говорил — использовать не чистый редакс, а кастомные обертки.

                                        0
                                        Я, видимо, плохо понимаю что такое чистый редакс, стандартные соглашения работы с ним, и где он кончается и начинается кастомная обертка. Глупый разговор, короче, предлагаю прекратить.
                                          –1
                                          Я, видимо, плохо понимаю что такое чистый редакс, стандартные соглашения работы с ним, и где он кончается и начинается кастомная обертка.

                                          Видимо, да.


                                          Проблема не в глобальном сторе, а в глобально мутабельном сторе.

                                          Стор реакта мутабельный, т.к. стейт хранится в мутабельной переменной внутри этого стора.


                                          Выше в статье даже код есть, как этот стор обновляется:


                                          dispatch: action => { state = reducer(state, action) },

                                          Видите присваивание?

                                            –1
                                            Стор у _редакса_ (я надеюсь вы о редаксе, а не о реакте) такой, каким вы его сделаете. Сам редакс, как вы любите говорить, чистый редакс, не заставляет вас делаеть его мутабельным. Я же говорю глупый разговор. Вы прочли пару статей типа «Как сделать Хелло ворлд на редакс», с какогото перепугу решили что там описан эталонный чистый редакс и его бест практисез, которым необходимо следовать, писать руками ашены и криейторы, и т.п. и говорите о том что это не масштабируется. Я с вами согласен с одной оговоркой. Ни одна статья Хелло Ворлд не масштабируется. Ни одной не знаю. И не понимаю что такое «чистый редакс», начиная с того что не знаю ни одного проекта с использованием только редакса. Успехов!
                                              +1
                                              Стор у редакса (я надеюсь вы о редаксе, а не о реакте) такой, каким вы его сделаете.

                                              Нет, в редаксе есть конкретная реализация стора, состояние которого изменяется при помощи мутабельного вызова store.dispatch(action);


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


                                              с какогото перепугу решили что там описан эталонный чистый редакс и его бест практисез

                                              Окей, теперь буду знать, что на https://redux.js.org/ не описан эталонный редакс и его бест практисез.
                                              А где описан, не подскажете? Буду искренне благодарен за ссылочку.


                                              везде, где присваивание, там мутация?

                                              Везде, где присваивается другой объект :)

                                                0
                                                = Везде, где присваивается другой объект :)= Это не мутация самого обьекта. Все, я ухожу, нет времени на глупый разговор. Простите если что не так.
                                                  0
                                                  Это не мутация самого обьекта.

                                                  Ну так стору новый стор и не присваивается. Стор остается тем же самым, но внутри него меняется стейт на новый. То есть, у вас есть объект store = { state: initial } и вы делаете store.state = reducer(store.state, action);
                                                  Типичная мутабельная операция. Каноничная прям я бы даже сказал.

                                                    0
                                                    Это зависит от того, что у вас в редюсере. Бест практис — строить редюсер как фабрику для нового стора. Выше вы писали, что вычитали redux.js.org. Наверное и redux.js.org/faq/immutable-data#why-is-immutability-required-by-redux это читали ведь? Или просто троллите?
                                                      +1
                                                      Это зависит от того, что у вас в редюсере. Бест практис — строить редюсер как фабрику для нового стора.

                                                      Вы путаете стейт и стор. Стор — это та штука, на которой вы вызываете dispatch, и которая содержит стейт. Стейт — это поле внутри стора. Редьюсер возвращает новый стейт, который заменяет в сторе старый.
                                                      Так вот в редаксе:


                                                      1. стор — мутабельный
                                                      2. эту мутабельность никак не выпилить
                                                      3. та недоиммутабельность, что таки есть, ничего не дает самому программисту (в сравнении с той, которая, например, в хаскеле). совершенно без разницы, с точки зрения качества кода и удобства поддержки, поменяете ли вы стейт мутабельно или вернете новый. Возврат нового нужен только для того, чтобы можно было использовать нативные средства оптимизации реакта — более ни за чем.

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

                                                        0
                                                        Да, стейт и стор попутал. Но вам не дает, а мне дает. И даже не скажу сколько всего дает ))) Удачи с хаскелем на юай!
                                                          0

                                                          Никому не дает, потому что это строго эквивалентный код. Его можно прогнать через плагин и автоматически преобразовать даже из одного вида к другому, без каких-то особенных проблем.

                                                            0
                                                            Я в курсе про лямбда исчисление и подстановку. Никто вам не обещал чисто функционального подхода. И никто с вами не соглашался, что чисто функциональный подход важнее, нужнее или просто лучше любого другого в промышленности. Просто потому что это не так.
                                                              –2

                                                              Да пожалуйста. Это ведь никак не отменяет того, что опасность глобальных мутабельных объектов подтверждена уже почти полувековой (!) практикой.
                                                              У многих современных фронтендеров тогда еще папа не родился — а сообщество программистов уже знало, что это все зло, и знание это было записано кровью павших :)

                                                                +1
                                                                Ничего что обьекты window и document — глобальны и мутабельны? Полувековая практика, на которую вы молитесь, пользует их уже полвека, и ничего лучше не придумано.
                                                                  0
                                                                  Ничего что обьекты window и document — глобальны и мутабельны?

                                                                  Конечно же, чего, почему вдруг нет?


                                                                  и ничего лучше не придумано

                                                                  Просто жс был сделан на коленке за пару дней для двадцатистрочных скриптиков, и никто не предлагал, что его будут использовать когда-то как general purpose language. А потом стало поздно. Вот и приходится людям колоться, но жрать кактус.


                                                                  А так-то придумано миллион разного чего.

                                                                    0
                                                                    =Просто жс был сделан на коленке = Ага, а первый ПК в гараже. Никто не предполагал. Нет в жизни счастья, пойду давится кактусами, надо доплатить за третий моргаж и восьмую машину, хехе.
                                                          –1
                                                          та недоиммутабельность, что таки есть, ничего не дает самому программисту

                                                          Эта "недоиммутабельность" даёт возможность в принципе как-то использовать оптимизации реакта. Не понимаю почему вы считаете что она не нужна программисту.

                                                            0
                                                            Эта "недоиммутабельность" даёт возможность в принципе как-то использовать оптимизации реакта. Не понимаю почему вы считаете что она не нужна программисту.

                                                            Она дает программисту редакса, которому не надо ничего придумывать для оптимизации. Но не дает программисту, который редакс использует. Т.к. с его стороны в любом случае все будет оптимизировано, при любом подходе.

                                                –1
                                                дел
                                            +1
                                            Да я уже понял, пишите на обычных функциях свой велосипед каждый раз

                                            Итак имеем:
                                            — Глобальный стор (обычно все глобальное — плохо, а тут вдруг — хорошо).
                                            — Кучу экшенов где action.type уникален в пределах приложения.
                                            — Кучу action creator-ов c названием, в большинстве случаев, таким же как action.type, записанным в другом регистре.
                                            — reducer-ы — по сути обычный switch case.

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

                                            Вопрос:
                                            Где в этом хозяйстве архитектура, абстракции и изоляции и чем оно координально лучше, чем обычные методы хранилища? ЗЫ: Методы хотя бы можно комбинировать в отличии от словоблудия внутри switch case.
                                              0
                                              Проблема не в глобальном сторе, а в глобально мутабельном сторе. Не ВСЕ глобальное плохо. Существует миллион глобальных вещей в любой платформе. И так и нет ответа при этом, что же лучше, если не ванилла самописный велосипед, который конечно же лучше, потому что свой. Спорить даже лень. Что такое «Обычные методы хранилища?» Какие абстракции и изоляции в вашем велосипеде, только вам одному известно. О чем мы говорим? Критикуешь — предлагай. Нет предложений — лучше помолчи.
                                                +2
                                                Не ВСЕ глобальное плохо. Существует миллион глобальных вещей в любой платформе.

                                                Что-то не могу придумать/вспомнить ни одного случая, когда при наличии выбора глобальный/не глобальный выбор был бы в пользу первого. Может подскажите.
                                                И так и нет ответа при этом, что же лучше, если не ванилла самописный велосипед, который конечно же лучше, потому что свой. Спорить даже лень.

                                                Про ванилу в командной разработке вы конечно правы, но мне не понятно каким образом люди приходят к выводу что именно редакс является лучшим выбором. По моим наблюдениям обычно происходит приблизительно так:

                                                Сеогдня: О! Экшены, экшен-криэйторы, редьюсеры… это круто! Используем их.
                                                Завтра: Ээээ. Нам срочно нужна библиотека, которая уменьшит кол-во бойлерплейта и избавит нас от всех этих бессмысленных экшенов, экшен-криэйтеров и т.д.

                                                Сегодня: О! Single source of truth бенефит (так ведь это модно называть?) это круто. Он нужен нам!
                                                Завтра: Ээээ. А давайте использовать какой-нибудь reselect чтобы хоть как-то разбить это монолитное говно на части.

                                                Самое странное, что даже наступив на грабли, они продолжают одновременно придерживать обеих точек зрения (Single source of truth — отличная идея, но reselect мы все-таки используем). Шизой попахивает КМК.

                                                А вот те разработчики, которые прочитали хотя бы GOF, с порога понимают как все устроено и к чему это безумие приведет, но к сожалению таких почему-то меньшинство.

                                                ЗЫ: Я не про вас лично, а про frontend в целом.
                                                  0
                                                  = выбора глобальный/не глобальный выбор был бы в пользу первого. = Правильнее было бы задать вопрос откуда у людей вообще этот выбор. Не к тому что он не правилен, а где они проводят границу, когда использовать глобальный. Думаю согаситесь, что примеров неудачного выбора можно придумать тысячи и в пользу того, и другого, я не буду перечислять.

                                                  =Шизой попахивает КМК.= Это не шиза, это нормальное развитие. Не придумали еще ничего идеального, ни в смысле платформы, ни в смысле методологии/подхода в целом, вот и мечутся.
                                                    +1
                                                    Не придумали еще ничего идеального, ни в смысле платформы, ни в смысле методологии/подхода в целом, вот и мечутся.

                                                    Ну как сказать. По факту реакт и экосистема — это то, как писали на пхп в нулевых, то есть все это уже старо как мир, 15 лет назад обсосано, все эти проблемы исследованы, решения (в современных пых-пых фреймворках) найдены и классифицированы. Причем взяты они еще из разработки 90-х под десктоп.


                                                    Просто js только-только начался как полноценный язык с полноценной экосистемой, вот и проходит соответствующий цикл развития, ну программисты вынуждены быть контрамортами и жить в прошлом на четверть века назад :)

                                                      –1
                                                      Это не шиза, это нормальное развитие
                                                      Знаем мы такое развитие IRL. Их впоследствии неандертальцами обозвали ;)
                                                        0
                                                        У любого человека около 5% генома от неандертальца. Нельзя сказать что это мало, если учесть сколько всего процентов нас отличают от кольчатого червя, скажем. (это я шучу если что)
                                  +3
                                  Для крупных развивающихся приложений идея центрального стора довольно полезена. Особенно принцип single source of truth. Несколько раз бывало делал по-быстрому без стора некоторую часть функционала и каждый раз в итоге приходилось потом переделывать на стор (что сложнее чем изначально сделать на сторе), тк со временем стало сложно развивать и поддерживать штуковины где состояние изолированно хранилось в самих компонентах или где-то в разбросанных сервисах.
                                    +1
                                    Вы сейчас больше про flux говорите, в общем-то все верно. Но redux — это реализация, которая к этому добавляет:
                                    1. редьюсеры чистые функции (а значит мы не можем использовать внутри грязные функции типа ajax запросов)
                                    2. состояние иммьютебельно и едино (библиотеки и combineReducers конечно помогают, но код читать и отлаживать довольно тяжело)
                                      +1
                                      2. состояние иммьютебельно и едино (библиотеки и combineReducers конечно помогают, но код читать и отлаживать довольно тяжело)
                                      C github.com/mweststrate/immer можно менять стейт императивно, как-будто напрямую, а библиоетка сделают грязную работу за вас обрабатывая прокси дифф.

                                      Иммутабельность помимо прочего позволяет выполнять сравнение по ссылке при ченж детекшене, что при правильном использовани позволяет избежать лишних отрисовок и в целом значительно увеличить производительность.
                                        0
                                        — «мы не можем использовать внутри грязные функции типа ajax запросов» — для этого есть мидлвары. Разделение ответственности — это прекрасно.
                                        — «но код читать и отлаживать довольно тяжело» — мутабельный код отлаживать на порядок тяжелее. Я помню этот ужас, когда логируешь стейт в Alt.js, а он отображается текущим в консоле, а не на момент лога, это совсем не явно, потом приходилось глубокую копию делать, что бы продебажить нормально. Я бы и без редакса использовать иммутабельность — она не такая дорогая, но дебажить ее в разы проще.
                                          +4
                                          Наверное я просто не умею готовить редакс, но вот прям совсем не сложились отношения.

                                          > Разделение ответственности — это прекрасно.
                                          Разделение ответственности по функционалу прекрасно, а что прекрасного в разделение логически цельной функции на несколько? В чем преимущество здесь делать запрос отдельно?
                                            +4

                                            вот тоже, пока что только слышал восторженные отзывы, но когда сам пытался писать на react/redux то мозг вскипал на простых казалось бы штуках.

                                      –1
                                      изначально сделать на сторе

                                      Вот меня напрягяет когда простые вещи, имеющие отношение только к V начинают запихивать в store.
                                      Нам нужен сайдбар с одним единственным свойством collapsed. Для этого нам нужно (если следовать документации к redux):

                                      1. Придумать константу с названием действия.
                                      2. Написать ничего не делающий actionCreator.
                                      3. Добавить в reducer обработку действия.
                                      4. Написать компонент-контейнер подключающий Sidebar к Redux.

                                      И всё это в разных файлах. Пример конечно несколько утрированный, но это же программирование ради программирования.
                                        +1
                                        redux в каническом виде я никогда не использовал, преполагаю если следовать каким-то поверхностным гайдам, то бойлерплейта действительно может получиться много. Константы на экшены не нужны, так же как и классы, с помощью github.com/pelotom/unionize можно лаконично описать весь бандл экшенов в одном месте и этот же бандл испольщзовать для матчинга в ридьюсере вместо свичей + поддержка TypeScript (очень помогает контролировать сложность).

                                        Нам нужен сайдбар с одним единственным свойством collapsed.
                                        Бывает нужно например переключать это поле collapsed из самых разных мест или завязать на это же значение еще какие-то отрисовки кроме самого сайдбара и здесь центральный стор помогает очень. Особено удобно когда стор observable.
                                          –1
                                          Не использовал unionize, но выглядит не очень читаемо КМК.
                                          Константы на экшены не нужны
                                          Вроде бы да, но нет. В большом приложении будет очень много actionCreatoro-в и, рано или поздно, захочется разделить их на насколько файлов. При добавлении нового действия придется просмотреть все эти файлы и убедиться что действий с таким же action.type не существует.
                                          Бывает нужно например переключать это поле collapsed из самых разных мест...
                                          А в каком-нибудь, не любимым всеми, ExtJS это бы выглядело приблизительно так:
                                          function onTriggerClick() {this.down('sidebar').toggle()}
                                          И больше ничего.
                                            0
                                            Не использовал unionize, но выглядит не очень читаемо КМК.

                                            Дело не только в читаемости, но в поддежке TS. Есть много аналогов, но именнно эта библиотека сама по себе небольшая, но гибкая. Делать отдельные файлы на каждый экшен это действительно плодить много бойлерплейта. Мне удобнее объединять экшены в unionize бандлы группируя их по назначению. Правда бибиотека пока что не поддеривает префиксы для бандлов, но ее можно форкнуть и добавить.

                                            function onTriggerClick() {this.down('sidebar').toggle()}
                                            Полагаю вызов down ресолвит вложенный компонент и явно вызывает у компонента метод toggle. Но это ведь жесткая привязка к структуре дерева компонентов. Кроме того строгую типизацию такого сделать будет сложновато. Ну и в скорости ресолвинга не уверен.
                                              –1
                                              Делать отдельные файлы на каждый экшен
                                              Ну не так же радикально. Логически разбить по несколько экшенов в файл (в каком-нибудь TODO это действия относящиеся к пользователю и действия относящиеся к задачам).
                                              Полагаю вызов down ресолвит вложенный компонент
                                              Это только для примера. Перемещаться по дереву можно было в любом направлении.
                                        +2
                                        тк со временем стало сложно развивать и поддерживать штуковины где состояние изолированно хранилось в самих компонентах или где-то в разбросанных сервисах.

                                        Это верно только для очень маленьких приложений. Вообще же изоляция стейта ведет к снижению coupling, то есть полезна почти всегда. Вынос стейта в глобальную переменную — это всегда грязное решение, которое удобно в краткосрочной перспективе, но ведет к драматическим последствиям в долгой. Просто на долгую перспективу во фронетенде в 2019 обычно не работают.
                                        Правильное решение в случае проблем с взаимодействием модулей — переработка связей между модулями.

                                        –1
                                        Действительно ли нужны иммьютабельные состояния
                                        Да это больше похоже на костыль для реакта ибо
                                        — передавая в компоненты реакта мутабельные объекты вы не сможете нормально реализовать PureComponent.
                                        — Это приведет к реконсилированию (так оно кажется называется) практически всей ветки компонентов.
                                        — А это приведет к тормозам в приложениях сложнее HelloWorld.
                                          0
                                          Наоборот. Иммутабельность позволяет сравнивать данные просто по ссылке, вместо глубокого сравнения, что позволяет быстро пропускать ненужные тяжелые операции (как правило это отрисовка). Пересоздавать объекты на пути от дочернего поля до самой верхушки объекта нужно чтобы ссылки на объект поменялись, тк потребители стора / компоненты могут подписываться на изменения любого уровня вложенности на пути к дочернему изменяемому значению, а все что вложено в объект это его часть. Очевидно очень желательно делать структуру стора как можно более плоской.
                                            –1
                                            Хм… А я о чём? Об одном и том же говорим. Иммутабельность нужна в первую очередь реакту.
                                              0
                                              Ну почему только реакту, это общее понятие. Дропнуть тяжелую операцию много где полезно может быть. В ангуляре тоже для компонентов можно включить «on push» режим и использовать полезность иммутабельности.
                                                0
                                                это общее понятие
                                                Понятно что общее, но мы же только про Redux говорим.
                                                0
                                                Я не фанат react/redux, но стало интересно как вы предлагаете «понимать» что какое-то свойство объекта изменилось без иммутабильности?
                                                  +1
                                                  Вариантов не много:
                                                  — Использовать иммутабельные данные, лучше простых типов (примитивы + объект и массив), тогда можно просто сравнивать по ссылке. Вывод: дешевое сравнение.
                                                  — Делать deep checking. Вывод: дорогое сравнение, тормоза.
                                                  — Использовать обертку с методами get/set или что-то вроде прокси или Object.defineProperty и внутри творить магию. Вывод: магия, вероятно большее потребление памяти, хакнутая структура данные и тд.
                                                    0
                                                    Вообще, вопрос бы адресован rgs350 на тему, что иммутабельность нужна только реакту. Но все равно спасибо, что просветили)))
                                                    0
                                                    И тут мы плавно подошли к мысли, что один store, оповещающий все компоненты не такое уж и хорошее решение :)
                                                      0
                                                      Почему если стор иммутабельный?
                                                        0
                                                        Потому что оповещающий все компоненты.
                                                          +1
                                                          Ну так то стор глобальный, можно ведь придумать как не использовать один глабальный. Но в целом какая здесь проблема? Пускай оповещает все компоненты, реагировать логикой будут только те которые должны, остальные просто пропустят сигнал сравнив данные по ссылке что недорого.

                                                          Именно один глобальный стор и дает single source of truth бенефит.
                                                        0
                                                        один store, оповещающий все компоненты

                                                        Всё так, но мне кажется, тут важно отметить, что это фишка react-redux. И никто нас не сковывает в этом деле. Можно нарисовать свою древовидную систему обновлений, или воспользоваться уже какой-нибудь готовой.


                                                        Т.е. это не проблема подхода как такового. Это проблема конкретного решения.

                                                          0
                                                          Работать со списками элегантно всеравно не получится
                                                            0

                                                            Особенно если это списки на сотни элементов без virtualScroll-а :)

                                                          0
                                                          Причем тут мой вопрос и стор, который кого-то там оповещает? Я спросил чисто про иммутабельность. Вы сказали она нужна только для работы реакта, вот я и хочу узнать, может вы знаете другой способ понять изменилось ли свойство объекта.
                                                            –1
                                                            Мы ведь по прежнему говорим про иммутабельность применительно к редаксу?
                                                            вы знаете другой способ понять изменилось ли свойство объекта
                                                            А вы ответьте себе на вопрос зачем вам понимать что свойство изменилось? и сразу станет понятно
                                                            Причем тут стор, который кого-то там оповещает

                                                              0
                                                              Мы ведь по прежнему говорим про иммутабельность применительно к редаксу?

                                                              Нет, вы сказали про иммутабильность по отношению к реакт:

                                                              Иммутабельность нужна в первую очередь реакту.

                                                              Redux независимая библиотека, которую используют далеко не только в React проектах.

                                                              А вы ответьте себе на вопрос зачем вам понимать что свойство изменилось? и сразу станет понятно

                                                              Очевидно, чтобы применить это изменение к DOM. А вы знаете иной способ синхронизации стейта и его представления в DOM дереве?
                                                                +2
                                                                Оффтоп
                                                                Redux независимая библиотека, которую используют далеко не только в React проектах.
                                                                Если кто-то притащил эту либу в проект не связанный с реактом, то это совсем не означает, что она является оптимальным выбором. Просто у многих frontend-разработчиков на текущий момент наблюдается эффект Даннига-Крюгера совместно с тяжелейшим религиозным повреждением головы. Для эксперимента попробуйте порекомендовать С++/JAVA-разработчику использовать подходы из редакс в своей повседневной деятельности. Только наденьте средства индивидуальной защиты, поскольку его ответ будет находится где-то в интервале между плевком в лицо и нанесением тяжких телесных повреждений.

                                                                Очевидно, чтобы применить это изменение к DOM. А вы знаете иной способ синхронизации стейта и его представления в DOM дереве?

                                                                Без обид, но вам это очевидно по причинам, указанным под спойлером. Объясню простым примером:

                                                                1. Подход редакс.
                                                                — Есть стор с двумя свойствами a и b.
                                                                — Есть 2 компонента: A (использует свойство a) и B (использует свойство b).
                                                                — Изменяем свойство a.
                                                                — Стор оповещает компоненты A и B.
                                                                — Компонент А понимает, что свойство a изменилось и рендерится.
                                                                — Компонент B понимает, что свойство b не изменилось и не рендерится.

                                                                2. Подход не редакс.
                                                                — Есть 2 стора в одном значение a в другом значение b.
                                                                — Есть 2 компонента: A (использует свойство a) и B (использует свойство b).
                                                                — Изменяем свойство a.
                                                                … дальше сами можете догадаться.

                                                                Банальщину же обсуждаем.
                                                                  0

                                                                  На самом деле так можно придти к тому что у вас вообще все скалярные значения будут отдельными сторами. И по сути вы переизобретёте observer :) (redux это по сути 1 observable c заморочками)

                                                                    0
                                                                    Без обид, но под спойлером флуд, даже не оффтоп, поэтому пусть там и остается. Если вы считаете что я выгораживаю Redux или React, то вы ошибаетесь. Ни то, ни другое мне не близко.

                                                                    Однако ваши примеры отражают не понимание вопроса. Вот мой пример:
                                                                    — Есть объект с данными а N-ой вложенности.
                                                                    — Есть один компонент А, который его использует.
                                                                    — Изменяется свойство объекта а на неком уровне вложенности (пусть будет a.b.c).
                                                                    — Как нам оптимально применить это изменение в DOM?

                                                                    Ту банальщину, которую вы написали давайте обсуждать не будем.
                                                                      –1

                                                                      Неужели нужно подписаться на изменения объектов a, a.b и a.b.c?

                                                                        –1
                                                                        А вы в JS умеете подписываться на изменения объектов? Не знал.
                                                                          0
                                                                          Да, умею.
                                                                            0

                                                                            Здорово! Нам наверное не расскажете как?

                                                                              –1
                                                                              Просто храню список подписчиков и уведомляю их при изменении свойства же. Вы правда не знали что так можно?
                                                                                0
                                                                                Вы правда не поняли, что я спрашивал вас не об этом? Вам на вход приходит js объект, без proxy/getters-setters и других велосипедов, чистый JS. Вам нужно понять поменялся он или нет? Зачем вы рассказываете про самопальные подписки?

                                                                                Пока нам не завезли Object.observe (а его вероятно и не завезут), вы никак в JS не можете понять изменился ли объект. Используя иммутабельность можно хотя бы частично решить эту проблему.
                                                                                  +2
                                                                                  А с какой стати ко мне на вход приходит простой объект? Это же вопрос архитектуры. Если архитектура строится вокруг иммутабельности — то на вход приходят простые объекты. Если архитектура строится вокруг наблюдателей — то и на вход приходят непростые объекты.
                                                                                    –1
                                                                                    Чудно как-то у вас архитектура смешалась с языком. Я спрашивал про возможности языка, а вы про паттерны речь ведёте. Перефразирую вопрос — можем ли мы в JavaScript как то эффективно понять, что объект был изменён, кроме использования иммутабельности?
                                                                                      –1
                                                                                      Да, можем. Нужно лишь подготовить объект специальным образом (см. выше). Объект подготавливается именно что языковыми средствами.
                                                                                        –1
                                                                                        у вас архитектура смешалась с языком.
                                                                                        Вообще-то это и называется программированием. Например автор редакса (Дэн Абрамов) занимается абрамированием. А вы чем? :)
                                                                          0
                                                                          У вас какое-то свое, очень странное, представление о связи сторов, иммутабельности, компонентов и DOM-а. Поделились бы что-ли истиной, доступной почему-то только вам. ;)

                                                                          Ну и напишите что-нибудь вместо трех точек из моего предыдущего сообщения. А то у меня начинают закрадываться подозрения.
                                                                            –1
                                                                            Я вам ни слова не написал про стор. Вопрос бы адресован вообще не к нему.

                                                                            Вы написали, что «иммутабельность нужна в первую очередь реакту». Я вот и пытаюсь понять, как же вы тогда определеяете что объект был изменен? Целый тред развели из-за такого простого вопроса.
                                                                              0
                                                                              Да перестаньте вы уже лить воду и напишите ваше «экспертное» мнение.

                                                                              Фрагмент вашего предыдущего собщения:
                                                                              вы знаете другой способ понять изменилось ли свойство объекта

                                                                              Фрагмент моего предыдущего сообщения (дополненный):
                                                                              1. Подход редакс.
                                                                              — Есть стор с двумя свойствами a и b.
                                                                              — Есть 2 компонента: A (использует свойство a) и B (использует свойство b).
                                                                              — Изменяем свойство a.
                                                                              — Стор оповещает компоненты A и B.
                                                                              — Компонент А понимает, что свойство a изменилось и рендерится.
                                                                              — Компонент B понимает, что свойство b не изменилось и не рендерится.

                                                                              2. Подход не редакс.
                                                                              — Есть 2 стора в одном значение a в другом значение b.
                                                                              — Есть 2 компонента: A (использует свойство a) и B (использует свойство b).
                                                                              — Изменяем свойство a.
                                                                              — Стор со значеним а оповещает компонент А.
                                                                              — Компонент A рендерится.

                                                                              Где во втором подходе вы увидели слово «понимает»? Компонентам плевать на изменение данных, которые не имеют к ним отношения.

                                                                              Ваш пример с a.b.c:
                                                                              Если вашему компоненту нужно свойство c, но вы зачем-то передаете в него a, то… ну сами делайте выводы насколько это нормально. Так что приведите какой-нибудь реальный кейс для вашего примера. Но поскольку я уже знаю что вы напишете то оставлю это здесь:
                                                                              AES
                                                                              U2FsdGVkX19PiLnKL6SHSQNkAiMWCO56yBnZnjtr8d/S5djFMwZ9Xy7EH5K0x/ug
                                                                              kjfZYWkQU+iAp7qpacJXLYQgzka4Qw66ADNuCLsEoncKeLm/ohPEGdbbKfL6Y09L
                                                                              Расшифруем его после вашего сообщения :)
                                                                                –1
                                                                                Если вашему компоненту нужно свойство c,

                                                                                Я разве где-то писал что ему нужно свойство c? Ему нужен весь объект a. Если писать вашим языком — компонент A использует объект a.

                                                                                Вот вам кейс:

                                                                                <UserProfile user={user} settings={settings} />
                                                                                

                                                                                Оба пропса — это объекты.

                                                                                Воду льете вы. Говорите о каких-то компонентах, вне какого-либо контекста. Я же задал вам очень простой вопрос — знаете ли вы иной способ, кроме иммутабельности, чтобы эффективно определить, был ли изменен объект или нет? Если знаете, мне было бы интересно это узнать. Если нет, тогда иммутабельность нужна далеко не только Реакту.
                                                                                  –1
                                                                                  Не угадал. Думал что вы напишете про списки, гриды и т.д, но сути не меняет Я, наивно предполагаю, что внутри UserProfile есть еще компоненты, внутри которых есть еще компоненты и т.д в которых вы собираетесь использовать свойства из user и settings и именно здесь вам и нужна иммутабельность. Ой! Это ведь дерьмовый подход реакта прокидывать пачки свойств через все дерево компонентов.
                                                                                  Кстати если вложенность компонентов достаточно большая и свойства из user и settings испольльзуются только в самых глубоколежащих компонентах, то только что вы сделали все промежуточные уровни зависимыми от свойств, которые никакого отношения к ним не имеют. Так-то.
                                                                                    –1
                                                                                    Я, наивно предполагаю, что внутри UserProfile есть еще компоненты, внутри которых есть еще компоненты и т.д в которых вы собираетесь использовать свойства из user и settings и именно здесь вам и нужна иммутабельность.

                                                                                    Да, видимо вы наивны. Я вам привел полный пример. У меня очень простой профиль пользователя и мне подкомпоненты не нужны там.

                                                                                    Так как будете определять, что свойства user или settings изменились?

                                                                                    Ой! Это ведь дерьмовый подход реакта прокидывать пачки свойств через все дерево компонентов.

                                                                                    Я вам про React вообще не пишу. И про Redux не пишу. Причем тут они? Даже про компоненты стали писать вы.

                                                                                    Еще раз для самых наивных: Как эффективно определить изменился ли объект, без иммутабельности в Javascript?

                                                                                    Это краеугольный вопрос для многих других операций, кроме синхронизации стейта и DOM. Или вы наивно предполагаете, что безотносительно к React/Redux, вам никогда не нужно определять изменился ли объект JS?
                                                                                      0
                                                                                      Походу бесполезно.
                                                                                      Я вам про React вообще не пишу. И про Redux не пишу.
                                                                                      Ну почитайте что-ли первый комментарий или комментарий сразу перед этим и подумайте про какую иммутабельность и где идет речь.
                                                                                      Ну и если же мы про иммутабельность во фронтенде, то:

                                                                                      Цитата из википедии
                                                                                      В функциональном программировании все (в случае чистых языков) или почти все (в случае «нечистых» — англ. impure — языков) данные в программе, как локальные, так и глобальные, являются неизменяемыми. С одной стороны, это существенно повышает стабильность программ за счёт упрощения формальной верификации программ. С другой, это затрудняет решение ряда задач (из которых наиболее часто отмечается задача реализации интерфейса пользователя, который в своей сути представляет собой изменяемое состояние), что вынуждает усложнять системы типов языка — например, монадами или уникальными типами[en].

                                                                                      Продолжайте натягивать сову на глобус.
                                                                                        –1
                                                                                        Ну почитайте что-ли первый комментарий или комментарий сразу перед этим и подумайте про какую иммутабельность и где идет речь.

                                                                                        А причем тут первый комментарий? Я вам 5 раз написал, что мой вопрос про иммутабельность в JS в целом, а не относительно к Redux. Когда вы первый раз не поняли, еще ладно, но потом то чего не ответить на простой вопрос?

                                                                                        Ну и если же мы про иммутабельность во фронтенде, то:

                                                                                        Цитата хорошая, но не отражает конкретно вашего понимания применительно к особенностям JS. Делаю вывод что его просто нет. В этом случае, пожалуй, хватить вас мучать. Вижу что ваша «сова» устала натягиваться.
                                                                          0
                                                                          del
                                                          +1
                                                          У меня другой вопрос — а зачем вообще нужен редакс?

                                                          Редакс — это просто набор примитивов, из которых можно собрать решение для менеджмента стейта вашей системы (для применения сам по себе, без обработки напильником, редакс никогда не предназначался). Безусловно, можно все эти по-5-строчек написать и самому, и каждый раз переписывать, или иметь свой набор и из проекта в проект тянуть. Но поскольку реалии современного фронтенда таковы, что команды/проекты меняются чуть не по нескольку раз в год — желательно использовать библиотеку, которую все знают.

                                                            0
                                                            Вопрос был не в том, что такое редакс, а в том, что его подход крайне странный и избыточный в контексте управления UI. Я понимаю, что js был на переднем крае прогресса и было много разного рода поиска новых подходов и экспериментов — и это очень круто (без иронии)!
                                                            Редакс предлагает очень привлекательные принципы rajdee.gitbooks.io/redux-in-russian/content/docs/introduction/ThreePrinciples.html. Но в жизни оказывается много бойлерплейта, ломания головы над правильной структурой стейта, странные костыли и т.д.
                                                              0
                                                              Но в жизни оказывается много бойлерплейта, ломания головы над правильной структурой стейта, странные костыли и т.д.

                                                              Так я же вам и говорю — бойлерплейт возникает от того, что используются низкоуровневые примитивы сами по себе. Если оборачивать их в абстракции — тогда он уходит.


                                                              А ломания головы над структурой стейта будут всегда, это сложная задача и не может быть магической кнопки, которая за вас ее решит.

                                                                0
                                                                Я имел ввиду redux по сравнению с другими подходами к программирования UI (например Angular, Vue или из других областей WinForms, WPF, да даже другие реализации Flux)

                                                                Там бойлерплейта в разы меньше, стейт не надо так продумывать, о чистоте обработчиков (редьюсеров) заботиться, об иммьютабельности и т.д.

                                                                И вопрос заключается в том перекрывают ли плюсы редакса те неудобства, что он создает по сравнению с другими подходами.
                                                                  –1
                                                                  Я имел ввиду redux по сравнению с другими подходами к программирования UI (например Angular, Vue или из других областей WinForms, WPF, да даже другие реализации Flux)

                                                                  Ну и в других подходах все ровно то же самое.


                                                                  Там бойлерплейта в разы меньше, стейт не надо так продумывать, о чистоте обработчиков (редьюсеров) заботиться, об иммьютабельности и т.д.

                                                                  Если вы прикрутите редакс к ангуляру или вуе — точно столько же будет бойлерплейта, очевидно. Вы просто сравниваете систему для контроля стейта с ее отсутствием. А надо сравнивать одну с другой. Когда ваш проект начнет расти — вы все равно какую-то систему такую прикрутите, вопрос лишь в том — будет ли она вашим велосипедом или известным всем решением.

                                                        +3
                                                        Как человек давно и вполне успешно занимающийся велосипедостроением, я не могу понять как такую простую вещь как стейт-менеджмент уровня Redux можно было реализовать настолько сложно. Блин, такие вещи как модуль с хранилищем и паб-сабом пишутся на коленке и прекрасно работают без кучи этих безумных свитчей, экшенов и редьюсеров. При работе со сложными графоподобными данными с Redux вы быстро упираетесь в ограничения такой модели, в простых — делаете кучу лишних телодвижений. Зачем?
                                                          0
                                                          Вы можете заметить даже по этой статье, что не так уж сложно реализован стейт-менеджмент у Redux. По ограничениям — на нашем проекте достаточно комфортно себя чувствует себя модель в 70кб данных и ~300 условиями в reducers. А реализация redux по моему скромному опыту для больших состояний оказывается удобнее, чем паб-саб
                                                            +1
                                                            Боюсь сложность данных определяется не размером в килобайтах, а отношениями между сущностями их составляющими (пересекающиеся связи, различные источники и т.д.). И, говоря про сложность Redux, я говорю не о сложности технической реализации, а о привнесенных абстракциях, и о работе с ним непосредственно. Для сложных случаев я использую графы, для простых — паб-саб.
                                                              +1

                                                              Всё решаемо. У меня довольно тесно переплетённый граф данных сейчас в приложении (редактор расписаний в школе, очень много взаимосвязей). Поэтому есть деревья селекторов (это такие кеш-функции для просчёта чего-угодно на основе store-данных). Всё "летает", несмотря на то, что некоторые штуки вычисляются на лету, скажем, при drag-n-drop. Но приходится использовать обильно мозг при построении архитектуры этих самых селекторов и работы со store-ом в целом. Всё, разумеется, иммутабельно. В принципе удобно. И очень легко дебажится, работает очень предсказуемо. Тут самое главное в архитектуре стора не ошибиться сильно. Любой серьёзный рефакторинг в этом деле — боль. Забыл добавить, данные в сторе нормализованы. Плюс используются мемоизация на основе weakMemoze, включая вложенные weakMemoize-ы. А для reducer-ов пишется "аля-мутабельный" код с proxy (т.е. по факту иммутабельный). В общем никакого криминала, когда уже набил руку на предыдущих проектах.


                                                              Вначале я попробовал реализовать этот же проект на Vue. Я столкнулся с очень серьёзной проблемой в tracking dependencies механизме и в итоге отказался от Vue. Думаю что для проектов с большим графом данных я Vue больше выбирать не буду. Были варианты остаться и использовать вместо computed скажем watchers, или вообще притащить туда что-нибудь типа rxJS… но зачем мне тогда Vue? :)

                                                                0
                                                                Я столкнулся с очень серьёзной проблемой в tracking dependencies механизме и в итоге отказался от Vue.
                                                                Если обобщить во Vue слишком много неподконтрольной «магии»?
                                                                  0

                                                                  Попробую кратко описать ту проблему:


                                                                  • есть 5000+ observable values
                                                                  • есть computed A, который их перебирает
                                                                  • есть computed B, который делает "A + 1"

                                                                  Что происходит в случае KnockoutJS, если A уже был просчитан ранее, а B нет?


                                                                  • knockout дёргает закешированное значение от A
                                                                  • knockout связывает напрямую B с A, чтобы работала реактивная магия

                                                                  Что происходит в случае с Vue?


                                                                  • vue дёргает закешированное значение от А
                                                                  • vue связывает B с A.dependecies.*, коих 5000

                                                                  Итого O(1) vs O(n). Такое происходит с любыми computed какие только в проекте есть и завязаны на эти A. А у меня дерево таких. Плюс в моём случае это всё рендерится в таблице, где может быть до 160 ячеек разом. И каждая ячейка помимо render-method-computed может содержать ещё всякие другие computed. И все они непременно дёрнут те самые 5000. Думаю излишне говорить, что это начинает просто нещатно тормозить даже на самых малых выборках. А проект предполагал куда более сложные связи.


                                                                  В итоге я заменил Vuex на Redux, Vue\Vuex Computed на Reselect, Vue Components на React Components. По сути практически та же кодовая база, но всё работает молниеносно. Ну и очевиднее в разы :) И ещё есть куда лихо заоптимизировать если будет мало.


                                                                  За точный механизм работы Vue и Knockout не ручаюсь. Уже успел всё подзабыть. Пишу по старой памяти. Если чего нахимичил — прошу сильно не пинать :)

                                                                    +1
                                                                    Если не секрет, то почему вы с vue в таком случае переходили на react+redux, а не на react+mobx?
                                                                      0

                                                                      Ответ прост: дедлайны наступали на пятки. Взял то, что умею хорошо готовить. В режиме copy-paste понадёргал с прошлых проектов что надо и просто методично шаг-за-шагом переписал. Интерес к mobX питаю, но пока ещё с ним не работал. Уверен, там как и везде, есть свои тонкости. А я итак исчерпал все резервы времени к тому времени )

                                                                      +3

                                                                      В mobx-е описанной вами проблемы с компьютедами нет. Там компьютеды не только вычисляются иерархически но и решают проблему "ромба" (дублирующие вычисления при ромбовидных зависимостях) и условных подписок (когда зависимости меняются во время вычисления и могут быть лишние запуски). В общем используемый алгоритм гарантирует что при любых ромбовидных или условных зависимостях запуск компьютеда произойдет только один раз в цепочке вычислений (и также дополнительно можно заврапить в транзакцию чтобы множественные изменения внутри функции схлопнулись в один запуск цепочки вычислений). Подробнее про алгоритм можете почитать в этой статье и посмотреть примерную реализацию тут или тут или с дополнительными оптимизациями тут

                                                                        –3
                                                                        А можно сразу взять vue, вместо связки mobx + react
                                                                          +2
                                                                          Разумеется. Переходя с vue на что-то ещё из-за проблем с производительностью, нужно переходить именно на vue :-)
                                                                            0

                                                                            Вы комментарии читаете в случайном порядке? :)

                                                                          +1
                                                                          Было бы очень интересно об этом подробнее в виде статьи почитать
                                                                        0

                                                                        Понятно что все решаемо, непонятно зачем колоться об этот кактус если все можно сделать проще и красивее.

                                                                          0

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


                                                                          По redux/flux есть много учебных материалов (скажем "пятиминутка React"), которые показывают и рассказывают, что да как. Спустя 4-5 проектов на Redux мне всё ещё кажется, что с ним всё довольно очевидно делается. Не приходится ломать голову. Этот деревянный простой как грабли подход сам вынуждает делать удобную архитектуру.


                                                                          ИМХО

                                                                          0
                                                                          А как быть, если есть большая связность данных, но при этом чуть ли не на каждое действие нужно делать запрос к api и менять store?
                                                                            0

                                                                            Примерно так:


                                                                            • Нормализация данных в с store
                                                                            • redux-thunk | redux-saga | custom middleware | 100500 других решений
                                                                            • DRY

                                                                            В любом случае решения без внешнего store и с observable будут проще. Т.к. всё расположено прямо по месту. Flux заставляет разделять виды действий\команды, их реализацию и места применения. Redux ещё рекомендует держать конечные View предельно тупыми. Это всё неизбежно приводит к разбуханию кода. Но в средне и долгосрочной перспективе это сильно помогает. Это как строгая типизация — добровольные кандалы, которые дают определённые преимущества. Вопрос лишь в том, дают ли они именно вам больше, чем отнимают.

                                                                      0
                                                                      Конечно можно много что сделать на коленке, однако для индустрии лучше когда подобные вещи стандартизированы, так новички быстрее становятся полезными в конвеерной разработке.
                                                                        0
                                                                        Во первых, собственное решение отнюдь не означает отсутствия стандартизации и документации, и описать 2 метода типа State.publish(path value) и State.subscribe(path, handler) — совсем не сложно. И на то чтобы понять, как это работает у любого новичка уйдет ровно одна минута, в отличие от Redux. Во вторых, я не то чтобы призываю всех к тотальному велосипедостроению, я просто недоумеваю почему наиболее популярным стало именно такое, нелепое на мой взгляд, решение как Redux.
                                                                          0
                                                                          Вы своим решением предлагаете компонентам-пользователям состояния следить за изменениями в состоянии самостоятельно — через subscribe на изменения. Именно от этого уходил React, например. React компонентам плевать на то, откуда прилетело изменение, они знают только о факте изменения состояния и перерисовываются на основании свежего изменения. Но вообще subscribe у redux есть, если очень хочется
                                                                            +2

                                                                            Определить в одном месте функцию-обработчик — это, по вашему, следить самостоятельно? А Redux стало быть магическим образом значения привязывает и для этого ничего делать не надо? Ох.

                                                                              –1
                                                                              Вы видимо хорошо разбираетесь в Redux-e. У меня к вам есть небольшой вопрос:
                                                                              Представьте себе приложение со сложным интерфейсом и огромным количеством возможных действий пользователя, построенное на обсерверах, которое при каждом действии этого пользователя оповещает все компоненты на странице (даже те компоненты, которые текущее изменение состояния не затрагивает).
                                                                              Как бы вы оценили архитектуру такого приложения?
                                                                                0
                                                                                Правильные компоненты просто не будут реагировать на неревантные сигналы просто сравнив текущие и новые данные по ссылки тк иммутабельный стор это позволяет сделать. Сравнив используя встроенное в мемоизированные селекторы сравнение или в явном виде используя distinctUntilChanged-like подход.

                                                                                PS не пишу именно о Redux, но в целом о подходе.
                                                                                  0

                                                                                  Тут главная засада — не допустить через чур большого кол-ва connect-утых компонент. Т.к. mapStateToProps будут вызываться для всех из них всегда. Приходится включать голову и как-минимум группировать такие вещи.

                                                                                    0
                                                                                    В реакте вроде принято коннектить только контейнер и в нем уже делать «роутинг» данных на компоненты нижнего уровня через атрибуты/свойства?
                                                                                      0

                                                                                      В двух словах примерно так и есть. Просто часто возникает соблазн на уровнях ниже тоже понаклепать своих собственных контейнеров. Далеко не всегда это приемлемый путь. А проброс всего чего надо в достаточно глубоком дереве — штука тоже… неудобная. Частично может выручить context. В любом случае, когда мы имеем достаточно сложный UI с сотнями и тысячами всяких элементов, тогда нужно хорошо продумывать эти вот сочленения со store-ом. Иначе оно высоко и далеко не полетит.

                                                                                      0
                                                                                      Где-то года 2 назад в репозитории react-redux ребятки писали, что не нужно стесняться использовать connect. Раньше и правда рекомендовалось использовать несколько «контейнеров», подключенных черех connect, и пробрасывать дочерние пропсы через React компоненты. Сейчас создатели заявляют, что connect достаточно производителен, чтобы использовать его (без фанатизма) в больших количествах
                                                                                        +1

                                                                                        Узким местом является асимптотика такого решения. То что могло отнимать O(1) с тяжёлой константой (observer), отнимает O(n) с лёгкой константой (immutable + shallow comparions). Вопрос в N (число коннектов). Сами shallowComparison то быстрые. Но это не отменяет того, что:


                                                                                        • когда 99% времени библиотека считает что-нибудь за-зря, мы как минимум съедаем аккумуляторы конечных устройств
                                                                                        • всегда есть очень медленные девайсы, и узкие случаи, которыми мы пренебрегли, не заметили и т.д., которые могут выродиться во что-то ну совсем несуразное
                                                                                        • не всегда это можно быстро и легко исправить.

                                                                                        Т.е. я бы всё же стеснялся использовать connect всякий раз когда вижу какие-нибудь вещи в циклах и предполагаю там 50+ элементов. По правде я "стесняюсь" и при куда меньших масштабах. Но это уже моя паранойя )

                                                                            +2
                                                                            Потому что в юношеской, нонконформистской, среде фронта, библиотеки выбирают сердцем, а не головой. А Ден Абрамов — он же рок-звезда.

                                                                            Если аккуратно перенести всю концепцию Elm Architecture на JS, а не ее треть, и аккуратно продумать как удобно положить на JS — была бы хорошая, хотя и довольно нишевая, вещь. Но Денис написал 7 строк кода, и побежал пиарить это на весь мир. А нам теперь каждому первому джуну объяснять почему мы не пишем на Redux-е.
                                                                              0
                                                                              А что вместо него?
                                                                                +1
                                                                                Мы пилим энтерпрайзные бизнес-аппы. Там всякие дашборды, каталоги, скрины с поиском-фильтрацией, формы-визарды.

                                                                                Юзаем Apollo + удобняшки.

                                                                                — Apollo. Замечательно работает для UI, где мы больше показываем, чем редактируем (как тот же хабр). На чтение — Apollo пашет просто отлично, код чисто декларативный. Плюс GraphQL очень сильно помогает на бекенде — даже больше чем на фронте. Мутации — тоже в целом ок, но до момента когда начинается всякий CRUD, формы, optimistic updates.
                                                                                — самодельные формы. Приделываются к Apollo, и дополняют его. На момент редактирования, стейт закидывается в компонент Form, который организует data binding через линзы, валидацию, всякие undo/redo/cancel. По save — стейт кидается в мутацию, и погнали дальше через Apollo, а сама форма помирает.
                                                                                — какой-то временный стейт типа фолдинга — мы часто делаем по-классике, просто через this.state. Часто храним весь viewstate скрина прям в URL — скажем, состояние фильтров на каталогах.

                                                                                Вместе эти штуки уже закрывают 90% наших нужд.

                                                                                Есть несколько хитрых и сложных скринов, уровня того же Trello. Такие случаи нормально на Apollo не ложатся. Там мы велосипедим что-то типа Redux, но не совсем. Скажем, на одном таком скрине, UI синхронно работает с immutable-стейтом, а штука сбоку и в фоне — асинхронно синхронизирует этот стейт с сервером.
                                                                                  0
                                                                                  Раз уж вы в теме, не могли бы рассказать, почему Apollo, а не Relay?

                                                                                  Сам участвовал в нескольких проектах на Redux, MobX и даже на mobx-state-tree. Теперь вот посматриваю в сторону GraphQL. Но тут как всегда в экосистеме React — две конкурирующие технологии =(
                                                                                    +1
                                                                                    Вот хорошее сравнение: www.prisma.io/blog/relay-vs-apollo-comparing-graphql-clients-for-react-apps-b40af58c1534

                                                                                    У нас изначально не было Babel (был голый TypeScript), а Relay сразу его требует — чтобы компилировать запросы. Поэтому мы его сразу отсекли. Сейчас я думаю что лишняя строгость в схемах — может быть и плюс, а Babel все равно теперь нужен — даже с TypeScript-ом. Так что может Relay и ок, но нам уже смысла нет менять коней.

                                                                                    А вообще, про Apollo/Relay/Redux/MobX — надо понимать что нет серебрянной пули для state management-а. Нельзя выбрать одно решение и использовать исключительно его. Даже на уровне одного приложения может быть 9 скринов идеально ложащихся на Apollo; и один скрин на который Apollo не лезет. Но это же не значит что для тех 9-ти скринов надо писать 100500 редьюсеров и врукопашную менеджить кеш и isLoading-состояния.
                                                                            +1
                                                                            Есть хороший видео курс на egghead от автора redux — egghead.io/courses/getting-started-with-redux
                                                                            Пара уроков там посвящено тому как реализовать свой redux
                                                                              0
                                                                              Точно так же изучал в своё время redux — просто прочитав исходники и написав свою совместимую версию на ts github.com/keyten/redux-ts/blob/master/index.ts

                                                                              Только вот жаль, это не очень прояснило обычные паттерны его использования.
                                                                                0
                                                                                } catch(e){}
                                                                                Как-то такое не выглядит готовым для продакшена.

                                                                                Листенеры в таком виде не особенно полезны, было бы добно подписаться на изменение определенного участка стора. Можно сделать гибкую штуку на BehaviorSubject из RxJS и кода тоже будет мало не считая конечно саму библиотеку RxJS.

                                                                                О том что возможности TypeScript совсем не используются писать не буду в деталях, тема широкая. Но в целом использование нетипизированных Object и Function нивелирует практически все преимущества TS, лучше тогда уже просто JS взять.
                                                                                  +1
                                                                                  Вы не поверите, но именно это написано в коде Redux :)
                                                                                  try {
                                                                                      isDispatching = true
                                                                                      currentState = currentReducer(currentState, action)
                                                                                  } finally {
                                                                                      isDispatching = false
                                                                                  }
                                                                                    0
                                                                                    Дикость какая-то. А как понять что в ридьюсере возникла ошибка, в явном виде ставить try/catch в самих ридьюсерах?
                                                                                      +2
                                                                                      > Вы не поверите, но именно это написано в коде Redux
                                                                                      Написано не это, тут catch вообще нет — следовательно и перехвата ошибки нет. Только гарантированное действие в finally
                                                                                        0
                                                                                        И я проглядел как-то. Раз они ошибки не скрывают тогда все нормально.
                                                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                                                    +2
                                                                                    Это было время бурного развития фронтенда, время поиска нового. Библиотек и подходов было огромное количество, redux просто завирусился. Как сказал Jacobs молодежь выбирает сердцем, а не головой habr.com/ru/post/439104/#comment_19724582

                                                                                    Сейчас уже можно выбрать более адекватные инструменты. Мне лично очень нравится vue (и vuex — если нужен централизованный стейт). Он очень похож на react+mobx. Мне кажется, таким и должен был быть react изначально.

                                                                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                                  Самое читаемое