Привет, на связи Antihype JS. Продолжаем цикл статей по разбору стейт менеджеров. Сегодня у нас на обзоре наделавший шуму в сообществе Effector.js.В отличие от других подобных библиотек, команда Effector позиционирует свое решение не как стейт менеджер, а как инструмент для описания бизнес-логики приложения.Effector предлагает возможность описать бизнес-логику на том же языке, на котором общается команда разработчиков продукта, используя базовые примитивы: Event, Store, Effect.Библиотека является framework agnostic решением, может работать с React, Vue и даже Solid.js, используя соответствующие binding пакеты.Effector построен вокруг концепции связи между атомарными сторами и событиями, использует иммутабельный подход к данным и имеет хорошую поддержку TypeScript.Примитивы EffectorStore - основной объект для хранения данных. Рекомендуется декомпозировать ваши данные на большое количество сторов и связывать при необходимости. По конвенции имена всех сторов начинаются с $.import { createStore } from 'effector' type User = { name: string } const $user = createStore<User | null>(null)Event - способ сообщить о каком-либо событии в приложении или просто изменить состояние стора. При срабатывании запускает каскад последующих вычислений в приложении.import { createEvent } from 'effector' type User = { name: string } const userCreated = createEvent<User>()Effect - контейнер для всевозможных сайд-эффектов. Как правило эффекты используются для асинхронных операций. Эффект имеет встроенные события успешного завершения, ошибки, а также индикатор того что эффект находится в процессе выполнения. В качестве первого аргумента принимает обычную функцию:import { createEffect } from 'effector' type User = { name: string } const createUser = () => new Promise<User>((res) => res({name: 'user'})) const createUserFx = createEffect<void, User>(createUser) // событие успешного завершения, отдает данные createUserFx.doneData // событие неуспешного завершения, отдает объект ошибки createUserFx.failData // boolean стор индикатор // содержит true когда хотя бы 1 createUserFx // находится в процессе выполнения createUserFx.pendingСвязи между примитивами и написание логикиМы разобрали необходимые примитивы для работы с Effector. Давайте напишем небольшой пример программы, которая будет отправлять состояние счетчика на сервер, когда его значение будет равно 5.Для начала создадим стор для нашего счетчика и событие инкремента:import { createStore, createEvent } from 'effector' const inc = createEvent() const $counter = createStore<number>(0)Чтобы изменить состояние стора по событию мы будем использовать метод on. Для логирования состояния в консоль будем использовать метод watch:import { createStore, createEvent } from 'effector' const inc = createEvent() const $counter = createStore<number>(0) $counter.on(inc, (counter) => counter + 1) $counter.watch((counter) => console.log('counter:', counter)) inc() inc() // В консоли: // counter: 0 // counter: 1 // counter: 2Метод on принимает событие и функцию для обновления состояния. При срабатывании события inc:Запускается функция для обновления стейта с предыдущим значением стора.После обновления состояния стора срабатывает метод watch, происходит вывод текущего значения стейта в консоль.Теперь добавим эффект сохранения состояния счетчика на базе фейковой функции. Логируем приходящий аргумент и резолвим промис через 500 миллисекунд для имитации выполнения запроса:import { createStore, createEvent, createEffect } from 'effector' const inc = createEvent() const saveCountFx = createEffect((count: number) => { return new Promise((res) => { console.log('saveCountFx:', count) setTimeout(res, 500) }) }) const $counter = createStore<number>(0) $counter.on(inc, (counter) => counter + 1) $counter.watch((counter) => console.log('counter:', counter))Осталось добавить ключевой элемент всей библиотеки - оператор sample. Этот метод позволяет запускать какую-либо логику при срабатывании событий. В нашем случае, нам необходимо запустить эффект при состоянии счетчика равном 5:sample({ source: $counter, filter: (counter) => counter === 5, target: saveCountFx, })В качестве источника данных source используется наш стор $counter. В качестве сущности для последующего запуска target используется эффект saveCountFx. Для соблюдения условия запуска используется свойство filter. В filter передается функция, которая получает текущее значение source. Такую запись можно перефразировать следующим образом:Когда $counter обновит состояние, то проверить, что его значение равно 5. Если условие выполняется, то запустить saveCountFx со значением из $counter.Также с помощью sample можно задать событие clock, по которому следует запускать нашу логику:sample({ source: $counter, clock: buttonClicked, filter: (counter) => counter === 5, target: saveCountFx, })Когда событие buttonClicked будет вызвано, то проверить что значение $counter равно 5. Если условие выполняется, то запустить saveCountFx со значением из $counter.Наша задача с сохранением значения счетчика будет выглядеть следующим образом:import { createStore, createEvent, createEffect, sample } from 'effector' const inc = createEvent() const saveCountFx = createEffect((count: number) => { return new Promise((res) => { console.log('saveCountFx:', count) setTimeout(res, 500) }) }) const $counter = createStore<number>(0) $counter.on(inc, (counter) => counter + 1) sample({ source: $counter, filter: (counter) => counter === 5, target: saveCountFx, }) saveCountFx.doneData.watch(() => console.log('doneData')) saveCountFx.pending.watch((pending) => console.log('pending:', pending)) inc() inc() inc() inc() inc()Мы добавили вызов события инкремента 5 раз, подписки на события выполнения и индикатор выполнения операции. Результат в консоли:// pending: false // pending: true // saveCountFx: 5 // doneData // pending: falseТестирование, SSR, экосистемаПолный гайд от сообщества по тестированию с EffectorПакет для SSR на Next.jsРепозиторий с дополнительными пакетами для Effector от сообщества: формы, eslint плагины, библиотеки утилитЗаключениеМы сделали обзор на основные концепции и работу с библиотекой Effector. Использовать его в проде или нет - как обычно, решать только вам. Важно отметить, что Effector имеет большое русскоязычное сообщество.Если вам понравился контент и вы интересуетесь фронтенд-разработкой, то подписывайтесь на наш телеграм-канал.
Братан, хорош, давай, давай, вперёд! Контент в кайф, можно ещё? Вообще красавчик! Можно вот этого вот почаще?
Братан, хорош
Братан, хорош, давай, давай, вперёд! Контент в кайф, можно ещё? Вообще красавчик! Можно вот этого вот почаще?