Про нюанс генераторов в сторах
Opened this issue · 7 comments
Mobx оборачивает функции-генераторы в сторах в промис (mobxjs/mobx/src/types/modifiers.ts#L44) что скорее всего не нужно, если они были добавлены туда умышленно как в примере ниже. Возможно стоит про это добавить. Либо в главу про асинхронные действия, либо в продвинутые темы. Следствием этого является необходимость вручную задавать аннотации для полей и методов стора
class Notifications {
items = [/* some notifications */]
constructor() {
makeObservable(this, {
items: observable,
shiftNotification: action
})
}
// Mobx оборачивает функции-генераторы в промис (https://vk.cc/c6k7Ua), а тут этого не надо
// поэтому получение ивента вынесено в отдельную ф-цию
shiftNotification = () => this.items.shift()
getNextNotification = function* () {
yield this.shiftNotification()
}
}
А можешь привести пример без отдельной функции? Что поломается?
В контексте Mobx и makeAutoObservable действительно функции со звёздочкой - это генераторы, для того чтобы прочитать значение оттуда нужны .next() / yield / await:
function *getNext() {
yield 1;
yield 2;
}
const generator = getNext();
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
Вот (почти) полный листинг компонента который показывает уведомления. В реальности он ещё отвечает за снекбары aka Notifications.
Схема такая: на закрытие попапа вызывается метод onRequestClose
который как раз и использует генератор из systemEventsStore
. Соответственно, если там будет промис этот код работать не будет
let eventsGenerator
const SystemEvents = () => {
const {systemEventsStore} = useStore()
const [isOpened, setOpened] = useState(false)
const [currentEvent, setCurrentEvent] = useState()
useEffect(() => {
if (systemEventsStore.messages.length > 0 && !isOpened) {
eventsGenerator = systemEventsStore.getMessage()
setCurrentEvent(eventsGenerator.next().value)
setOpened(true)
}
}, [systemEventsStore.messages.length, isOpened])
const closePopup = () => setOpened(false)
const onRequestClose = () => {
const {done, value} = eventsGenerator.next()
if (!done) {
setCurrentEvent(value)
return false
} else {
return true
}
}
return <>
<Popup
isOpened={isOpened}
className={css.notify}
onRequestClose={onRequestClose}
onClose={closePopup}
>
<NotifyHeader closeCallback={closePopup}>{currentEvent?.header}</NotifyHeader>
{
currentEvent?.body
? <NotifyBody>{currentEvent?.body}</NotifyBody>
: undefined
}
{
currentEvent?.footer
? <NotifyFooter actions={currentEvent?.footer} closeCallback={closePopup}/>
: undefined
}
</Popup>
</>
}
Следствием этого является необходимость вручную задавать аннотации для полей и методов стора
А если использовать makeAutoObservable(this, { generatorMethod: false })?
Эта функция следует правилам - конвертировать геттеры в computed, поля в observable, методы в actions, генераторы в flow
@kubk так работает без лишнего метода
UPD: но вообще мой пример был больше про факт трансформации генератора в промис, нежели про то, что придётся городить какие-то дополнительные костыли
Понимаю. Было бы классно как-то ужать всё это в простенький воспроизводимый пример, иначе в книгу тяжело это будет добавить. И объяснить зачем тут именно генераторы
Если я правильно понял, то можно назвать "особенности использования функций генераторов"