Event
Event (событие, эвент) это декларация намерения сделать что-либо: запустить вычисления, отправить сообщение другой секции приложения или обновить состояния в приложении. Одна из основных управляющих сущностей, при срабатывании запускает цепочки реактивных вычислений в приложении. Является юнитом
События можно вызывать как обычные функции (императивный вызов) а также подключать в различные методы api включая sample, guard и split (декларативное подключение). При императивном вызове принимают максимум один аргумент и всегда возвращают переданные данные
Структура
Методы
- map: создает производное событие на основе данных из исходного
- prepend: создает событие-триггер для преобразования данных перед запуском исходного эвента
- filterMap: создает производное событие на основе данных из исходного с возможностью отмены вызова
- watch: вызывает функцию с сайд-эффектами при каждом срабатывании события
Свойства
Примеры
- map - пример использования map
- prepend - пример использования prepend
- filterMap - использование nullable react ref с filterMap
- watch - пример использования watch
Методы
map
Создает производное событие на основе данных из исходного
Формула
declare const eventA: Event<T>
const eventB = eventA.map(/*fn*/(data: T) => S)
-> Event<S>
При вызове исходного события eventA
, функция-обработчик fn
будет вызвана с поступившими данными, после чего производный эвент eventB
будет вызван с результатом вычислений
eventA -> fn -> eventB
Аргументы
fn
:(data: T) => S
Функция-обработчик, которая будет вычислять данные для передачи в производное событие
eventB
на основе данных из исходного эвентаeventA
. Должна быть чистойАргументы
data
: Данные с которыми сработало исходное событиеeventA
Возвращает
Данные для передачи в производное событие
eventB
Возвращает
Новое, производное событие
Примеры
Пример использования map
import {createEvent} from 'effector'
const updateUser = createEvent()
const userNameUpdated = updateUser.map(({name}) => name)
const userRoleUpdated = updateUser.map(({role}) => role.toUpperCase())
userNameUpdated.watch(name =>
console.log(`Имя пользователя изменено на ${name}`),
)
userRoleUpdated.watch(role =>
console.log(`Роль пользователя изменена на ${role}`),
)
updateUser({name: 'john', role: 'admin'})
// => Имя пользователя изменено на john
// => Роль пользователя изменена на ADMIN
prepend
Создаёт событие-триггер для преобразования данных перед запуском исходного эвента. По сравнению с map, работает в обратном направлении
Формула
declare const targetEvent: Event<S>
const trigger = targetEvent.prepend(/*fn*/(data: T) => S)
-> Event<T>
При вызове события trigger
, функция-обработчик fn
будет вызвана с поступившими данными, после чего эвент targetEvent
будет вызван с результатом вычислений
trigger -> fn -> targetEvent
Аргументы
fn
:(data: T) => S
Функция-обработчик, которая будет вычислять данные для передачи в исходное событие
targetEvent
на основе данных эвентаtrigger
. Должна быть чистойАргументы
data
: Данные с которыми сработало событиеtrigger
Возвращает
Данные для передачи в исходное событие
targetEvent
Возвращает
Новое событие
Примеры
Пример использования prepend
import {createEvent} from 'effector'
const userPropertyChanged = createEvent()
userPropertyChanged.watch(({field, value}) => {
console.log(`Свойство пользователя "${field}" изменено на ${value}`)
})
const changeName = userPropertyChanged.prepend(name => ({
field: 'name',
value: name,
}))
const changeRole = userPropertyChanged.prepend(role => ({
field: 'role',
value: role.toUpperCase(),
}))
changeName('john')
// => Свойство пользователя "name" изменено на john
changeRole('admin')
// => Свойство пользователя "role" изменено на ADMIN
changeName('alice')
// => Свойство пользователя "name" изменено на alice
filterMap
Создает производное событие на основе данных из исходного с возможностью отмены вызова
Метод добавлен в effector 20.0.0
Формула
declare const eventA: Event<T>
const eventB = eventA.filterMap(
/*fn*/ (data: T) => S | void
)
-> Event<S>
При вызове исходного события eventA
, функция-обработчик fn
будет вызвана с поступившими данными, после чего, если функция вернула не undefined, производный эвент eventB
будет вызван с результатом вычислений
eventA -> fn -> eventB
Если требуется только фильтрация вызовов без трансформации данных, то оптимальнее использовать guard
Аргументы
fn
:(data: T) => S | void
Функция-обработчик, которая будет вычислять данные для передачи в производное событие
eventB
на основе данных из исходного эвентаeventA
. Должна быть чистойАргументы
data
: Данные с которыми сработало исходное событиеeventA
Возвращает
Данные для передачи в производное событие
eventB
либо undefined, если вызовeventB
не требуется
Возвращает
Новое, производное событие
Примеры
Использование с методами JavaScript возвращающими undefined
const listReceived = createEvent<string[]>()
const effectorFound = listReceived.filterMap(list => list.find(name => name === 'effector'))
effectorFound.watch(name => console.info("found", name))
listReceived(["redux", "effector", "mobx"]) // found effector
listReceived(["redux", "mobx"])
Использование nullable react ref с filterMap
Методы modal.showModal и modal.close - стандартные возможности dom-элемента <dialog>
Статья в MDN про showModal
import React from 'react'
import {createEvent, createStore} from 'effector'
const openModal = createEvent()
const closeModal = createEvent()
const openModalUnboxed = openModal.filterMap(ref => {
if (ref.current) return ref.current
})
const closeModalUnboxed = closeModal.filterMap(ref => {
if (ref.current) return ref.current
})
openModalUnboxed.watch(modal => modal.showModal())
closeModalUnboxed.watch(modal => modal.close())
const App = () => {
const modalRef = React.useRef(null)
return (
<>
<dialog ref={modalRef}>
<form method="dialog">
<fieldset>
<legend>Модальное окно</legend>
Нажмите для закрытия
<button onSubmit={() => closeModal(modalRef)} type="submit">
❌
</button>
</fieldset>
</form>
</dialog>
<button onClick={() => openModal(modalRef)}>
Открыть модальное окно
</button>
</>
)
}
watch
Вызывает функцию с сайд-эффектами при каждом срабатывании события
Формула
declare const event: Event<T>
event.watch(/*watcher*/ (data: T) => any)
-> Subscription
Аргументы
watcher
:(data: T) => any
Функция с сайд-эффектами, в качестве первого аргумента получает значение с которым было вызвано событие. Возвращаемое значение не используется
Возвращает
Subscription: Функция отмены подписки, после её вызова watcher
перестаёт получать обновления и удаляется из памяти. Повторные вызовы функции отмены подписки не делают ничего
Примеры
Пример использования watch
import {createEvent} from 'effector'
const sayHi = createEvent()
const stop = sayHi.watch(name => {
console.log(`Привет, ${name}!`)
})
sayHi('Боб')
// => Привет, Боб!
stop()
sayHi('Алиса')
// => ничего не произошло
Свойства
shortName
Имя события. Задаётся либо явно, через поле name
в createEvent, либо автоматически через babel plugin. Используется для обработки сущностей программно, например при использовании хуков домена
Формула
declare const event: Event<any>
event.shortName
-> string
sid
Стабильный идентификатор события. Задаётся автоматически через babel-plugin
Формула
declare const event: Event<any>
event.sid
-> string | null
Дополнительные методы
filter
Создает производное событие с возможностью отмены вызова
Более гибким способом фильтрации является guard, рекомендуется использовать именно его
Формула
declare const eventA: Event<T>
const eventB = eventA.filter(/*config*/ {fn: (data: T) => boolean})
-> Event<T>
При вызове исходного события eventA
, функция-обработчик fn
будет вызвана с поступившими данными, после чего, если функция вернула истинное значение, производный эвент eventB
будет вызван с теми же данными
Аргументы
config
: Объект конфигурацииfn
:(data: T) => boolean
Функция-предикат, которая определяет необходимость вызова производного события
eventB
возвращая истинное значение,должна быть чистой
Возвращает
Новое, производное событие
Объектная форма аргумента используется потому что event.filter(fn) был сокращённой формой filterMap
Примеры
Пример использования filter
import {createEvent, createStore} from 'effector'
const numbers = createEvent()
const positiveNumbers = numbers.filter({
fn: ({x}) => x > 0,
})
const $lastPositive = createStore(0).on(positiveNumbers, (n, {x}) => x)
$lastPositive.watch(x => {
console.log('последнее положительное значение:', x)
})
// => последнее положительное значение: 0
numbers({x: 0})
// ничего не произошло
numbers({x: -10})
// ничего не произошло
numbers({x: 10})
// => последнее положительное значение: 10