Mastodon

Структура моделей Effector


Во время работы, у меня есть три состояния:

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

Написание нового кода, я начинаю с проектирования внешнего интерфейса файла, с событий которые необходимых в этом блоке логики.

Какие-то файлы описывают user story, другие — сущности. Детали подхода для каждого из типов файлов несколько отличаются, но код однозначно должен читаться сверху вниз: определения, логика, детали реализации.

Model

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

User Story

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

Entity

Данные описывающие одну сущность и события ее модифицирующие. У таких моделей обычно нет сценария использования и они лежат в models/ директории фичи.

Структура

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

Порядок блоков в модели:

  1. Типы
  2. События
  3. Эффекты
  4. Сторы и вычисляемые сторы
  5. Логика в виде связей
  6. Детали реализации

Типы

type InputEvent = SyntheticEvent<HTMLInputElement>
type Form = { login: string; password: string; remember: boolean }

const MINIMUM_TIMEOUT = 100

События

export const pageMounted = createEvent<void>()
export const loginChanged = createEvent<InputEvent>()
export const buttonPressed = createEvent<ButtonEvent>()
const buttonTypePressed = submitPressed.map((event) => event.currentTarget.type)

const loginSaveFailed = createEvent<string>()

Эффекты

export const saveLogin = createEffect()
const saveLoginFetching = createFetching(saveLogin, “loading”)

export const loadLogin = createEffect()

Сторы и вычисляемые сторы

const $login = createStore(“”)
const $password = createStore(“”)

export const $isLoginValid =  $login.map(loginValidator)
export const $isPasswordValid =  $login.map(passwordValidator)
export const $isFormValid = eachTrue([$isLoginValid, $isPasswordValid])
export const $form = createStoreObject({ login: $login, password: $password })

Логика в виде связей

$login.on(loginChanged.map(trimEvent), (_, login) => login).reset(pageMounted)

sample($form, submitPressed).watch(loginUser)

loginUser.use(authApi.login)

Детали реализации

function trimEvent(event) {
  return event.currentTarget.value
}