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

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

Immer позволяет создавать новое иммутабельное состояние за счет мутации текущего.

А зачем? Какой смысл работать с неизменяемый состоянием так, будто оно изменяемое, если можно просто работать с изменяемым?


Для React напишем кастомный hook:

Чем это в итоге отличается от использования обычного useState и его производных? выглядит один в один.


у которого также есть метод getValue, с помощью которого можно получить текущее состояние

Использование getValue на observabl'ах — code smell и допустимо только в крайних случаях.


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

А зачем? Какой смысл работать с неизменяемый состоянием так, будто оно изменяемое, если можно просто работать с изменяемым?

Смысл в том что Immer это просто утилита


const x = {foo: {bar: 0}}

const y = {foo: {bar: x.foo.bar + 1}}

и


const y = produce(x, draft => draft.foo.bar += 1)

Концептуально ничем не отличаются.


Чем это в итоге отличается от использования обычного useState и его производных? выглядит один в один.

Тем, что данные из стора вы можете использовать в любом компоненте. Для примера можно посмотреть другие вариантами использования https://codesandbox.io/s/github/simmor-store/react-simmor/tree/master/examples


Использование getValue на observabl'ах — code smell и допустимо только в крайних случаях.

К каким проблемам может привести его использование в контексте текущего примера?

Смысл в том что Immer это просто утилита

Это понятно. Я говорю — зачем оно надо, если можно писать просто draft.foo.bar += 1?


К каким проблемам может привести его использование в контексте текущего примера?

Дело не в проблемах, дело в том, что если ваша логика построена на getValue, значит, вам вообще не нужны обсерваблы.


Они в общем-то и не нужны — в рамках семантики реакта ф-я рендер это как раз подписка на обсервабл.

Поработав с MobX некоторое время, для себя пришел к выводу, что лично мне проще оперировать последовательностью иммутабельных состояний (наподобие Redux), чем логикой мутабельного состояния (наподобие MobX)

Оперируйте, разрешаю.
class Store {
  @observable _stateHistory = [];
  @computed get state() {
    return this._stateHistory[this._stateHistory.length - 1];
  }

  updateState(draftState) {
    this._stateHistory.push(toJS(draftState));
  }
}

Логика для обмазывания этим других классов, чтоб каждое изменение состояния дёргало бы updateState — точно такая же, как и у вас.

А зачем? Какой смысл работать с неизменяемый состоянием так, будто оно изменяемое, если можно просто работать с изменяемым?

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

Статья не про мутабельность vs имутабельность, это отдельная большая тема.
Как я написал в начале, это мое личное предпочтение, на основе моего опыта. Например у вас в _stateHistory хранятся полные копии всех измененных состояний, в случае с иммутабельностью части состояний могли бы переиспользоваться.

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

Это вообще не имеет отношения к статье и тому, что вы в ней описываете. Наверните сверху на _stateHistory какой-нибудь Immutable.js — и будет переиспользоваться. Или напишите то же самое руками. Но это другая проблема, никак не связанная с управлением состоянием.
Наверните сверху на _stateHistory какой-нибудь Immutable.js

Зачем сверху наворачивать Immutable.js если с самого начала можно использовать имутабельность? Плюсы это тема для отдельной статьи, что то вы можете почитать например в документации Redux.

Зачем вам «с самого начала» использовать иммутабельность, если вы дальше целую статью накатали о том, как бы делать this.counter += 1?
Вы хотите делать this.counter += 1 и где-то в запасе иметь историю состояний? Имейте, вам никто не запрещает. Вы можете её иметь и крутить каунтер, или не иметь её и всё так же крутить каунтер. Механизм самого управления состоянием от этого не меняется.

К сожалению невозможно охватить все сразу. Да, для описания удобного кручения иммутабельного состояния ушла целая статья, про это собственно я и хотел написать, но возможно стоило уделить внимание и тем вопросам что вы озвучили. Думаю в будущем будет продолжение, а пока с остальным функционалам можно ознакомиться в репозитории https://github.com/simmor-store/simmor

Есть даже mobx-state-tree для таких целей.

С mobx-state-tree я не работал, но когда искал варианты, такое описание стора меня оттолкнуло.


types
    .model({
        name: types.optional(types.string, ""),
        done: types.optional(types.boolean, false)
    })
    .actions(self => ({
        setName(newName) {
            self.name = newName
        },

        toggle() {
            self.done = !self.done
        }
    }))
Да вы извращенец однако…
Вот вам пожалуйста, посмотрите как можно использовать MobX и выкиньте то, что вы тут написали из головы, не дай god кому-то потом придется работать с кодом который вы написали:
codesandbox.io/s/goofy-hamilton-b9jxv — Быстрое знакомство с MobX, все самое основное, в том числе асинхронщина.
codesandbox.io/s/frosty-dust-2mk5b — Эффективный рендер (более продвинутый точечный рендер с вложонстью) MobX vs Redux.
codesandbox.io/s/adoring-kare-pre3r — MobX 4 (если нужна поддежка IE) Эффективный рендер (более продвинутый точечный рендер с вложонстью) MobX vs Redux.
codesandbox.io/s/silent-tdd-o1x69 — reaction, autorun, computed.
codesandbox.io/s/pedantic-silence-xu0jd — Переиспользование логики, и добавление useEffect в классовые компоненты (аналог reactjs.org/docs/hooks-custom.html)
codesandbox.io/s/priceless-sun-wr5dx — (голый React) Переиспользование логики, и добавление useEffect в классовые компоненты (аналог reactjs.org/docs/hooks-custom.html)
codesandbox.io/s/boring-lake-wspo3 — голый React хуки на классах

Примеры с использованием функциональных компонентов:
codesandbox.io/s/gallant-mountain-3w6bc — Пример с функциональными компонентами используя хуки
codesandbox.io/s/cocky-euler-x9b6e — Context with MobX 2х сторонний обмен данными и оптимизированный рендер (из коробки MobX), без дополнительных memo
codesandbox.io/s/flamboyant-mclean-tzjfp — Не кривое замыкание в функциональных компонентах

Наглядный пример мутабильность vs иммутабильность (когда в деле объекты, а не примитивы):
codesandbox.io/s/vibrant-leftpad-jc56q — смотреть в консоль, рендер каких компонентов вызывается

codesandbox.io/s/upbeat-flower-v0455 — Мультиязычность простой и реактивный вариант имплементации

akita смотрели? местами похоже на ваш подход

Я за mobX и мутабельность хотя как по мне сам mobX это не совсем про управление стейтом, но если кому интересно посмотреть на иммутабельные альтернативы и по факту — управление стейтом, есть любопытная статья rxjs in practice

В redux проблему бойлерплейта (а как я понял с ним автор и борется) легко можно решит библиотекой от самих же создателей redux redux-toolkit, которая под капотом как раз использует Immer, позволяя в редьюсерах работать как-бы в mutable way
Вот пример


const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    increment: state => state + 1,
    decrement: state => state - 1
  }
})
const store = configureStore({
  reducer: counterSlice.reducer
})
document.getElementById('increment').addEventListener('click', () => {
  store.dispatch(counterSlice.actions.increment())
})

Ссылочка на документацию

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

Публикации

Истории