Skip to content

Instantly share code, notes, and snippets.

@zerobias
Last active June 19, 2024 13:02
Show Gist options
  • Save zerobias/13a899398db52c143ba736a2475ba5f5 to your computer and use it in GitHub Desktop.
Save zerobias/13a899398db52c143ba736a2475ba5f5 to your computer and use it in GitHub Desktop.
Dynamic spawn in effector

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

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

Итак, проблемы которые нужно решить проектируя апи:

Первая проблема

Как пробрасывать аргументы в фабрику, которая вызывается один раз?

Нужно так или иначе сделать все аргументы сторами и возникает вопрос как сделать это удобнее всего?

Можно сделать объект с дефолтными значениями, внутри колбэка они станут сторами:

const userModel = model({name: '', age: 18}, ({name, age}) => {
  const $profile = combine({name, age})
  return {$name: name, $age: age, $profile}
})

const alice = spawn(userModel, {name: 'alice '})
const bob = spawn(userModel, {name: 'bob ', age: 23})

Можно задавать то, что будет параметром изнутри колбэка, возвратом нужных сторов в отдельном поле params. Выглядит и ощущается как работа с prepend, если вы понимаете о чем я 😃 Поэтому рабочий вариант тот который описан выше

UPD 12.04.24: есть необходимость гарантировать, что апи будет расширяемым, поэтому предпочтительна объектная форма с отдельным полем для значений. Во время обсуждения в чате предлагалось название поля default, но что, если далее мы будем предлагать модели без опциональных полей, только с обязательными? В vue используется название props, можно использовать его:

const userModel = model({
  props: {name: '', age: 18},
  create({name, age}) {
    const $profile = combine({name, age})
    return {$name: name, $age: age, $profile}
  }
})

const alice = spawn(userModel, {name: 'alice '})
const bob = spawn(userModel, {name: 'bob ', age: 23})

Вторая проблема

Как хранить инстансы модели? Модель можно спавнить в реакт компоненте, тогда инстанс хранится в компоненте и удаляется вместе с анмаунтом, удобно, но подходит не всегда, нам еще как-то надо будет это всё тестировать, не так ли 😃

Тут так или иначе все равно начинает всплывать keyval в той или иной форме, возможно он должен быть больше похож на entity adapter из rtk? Не понятно

Например, можно спавнить по одному инстансу на каждый итем в сторе-массиве примерно как это делает хук useList:

const $usersData = createStore([
  {name: 'alice', age: 19},
  {name: 'bob', age: 23},
])
const users = list($usersData, userModel)

В примере выше и далее используется модель из первого фрагмента кода

Тогда, keyval бы выглядел так:

const $usersData = createStore({
  alice: {name: 'alice', age: 19},
  bob: {name: 'bob', age: 23},
})
const users = keyval({
  source: $usersData,
  model: userModel,
})

const User = ({id}) => {
  const {$profile: profileValue} = useModel(users, id)
  return <div>{profileValue.age}</div>
}

Если вам в этот момент начинает казаться, что чего-то не хватает, то вы полностью правы 😃

Третья проблема

Как апдейтить инстансы внутри keyval, как ссылаться на них, подключить к сэмплу?

Чтобы обновить возраст алисы, нужно в сэмпле указать в какой key идёт апдейт, как это сделать удобнее всего?

Обновим модель, добавим в нее эвент для обновления возраста:

const userModel = model({
  props: {name: '', age: 18},
  create({name, age}) {
    const updateAge = createEvent<number>()
    age.on(updateAge, (_, upd) => upd)

    const $profile = combine({name, age})
    return {$name: name, $age: age, $profile, updateAge}
  }
})

Тогда при использовании модели в keyval, можно будет наружу выставлять эвент с явным упоминанием key, будет выглядеть так:

const $usersData = createStore({
  alice: {name: 'alice', age: 19},
  bob: {name: 'bob', age: 23},
})
const users = keyval({
  source: $usersData,
  model: userModel,
})

const changeAliceAge = createEvent<number>()

sample({
  clock: changeAliceAge,
  fn: age => ({
    // используется для выбора инстанса
    key: 'alice',
    // данные которые отправятся в updateAge выбранного инстанса
    value: age,
  }),
  // эвент из модели user но общий для всего keyval
  target: users.updateAge,
})

В итоге, для выработки финального решения нужно решить, как будет выглядеть апи model, апи keyval и апи для передачи данных в инстансы

@zerobias
Copy link
Author

Текущая разрабатываемая версия документа: https://effector.notion.site/Effector-models-5cca5c90410c4707ab1cdaea37e0cbae?pvs=4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment