V4Fire / Client

V4Fire client core library
MIT License
22 stars 14 forks source link

Невалидный кэш геттеров в функц. компонентах после наследования контекста #1292

Closed shining-mind closed 1 week ago

shining-mind commented 1 month ago

Пример в ветке:

dlartagnan/func-cache

Есть функциональный компонент с системым полем itemStore, на которое ссылается геттер/сеттер item. Далее геттер item используется в геттере value. По умолчанию, itemStore равен undefined.

  1. Происходит рендер и геттер value кэшируется со значением undefined.

  2. На mounted из персистетного хранилища извлекается объект и присваивается в item. Происходит инвалидация кэша value.

  3. При ререндере опять кэшируется value со значением undefined. Затем наследуется контекст и itemStore переносится из старого контекста в новый.

  4. Потом срабатывает mounted, где опять ивзлекается тот же самый объект из хранилища и присваивается в item, где уже хранится тот же объект. Как следствие кэш не инвалидируются (потому что фактически ничего не изменилось).

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

Текущими средствами проблему можно решить так:

  1. Пометить itemStore как unique: true.
  2. Запрашивать данные до рендера, чтобы рендер происходил с теми данными, которые будут в компоненте после mounted.
shining-mind commented 1 month ago

Fix https://github.com/V4Fire/Client/pull/1289

shining-mind commented 1 week ago

Важный нюанс: в itemStore должна хранится ссылка на объект, между ререндерами ссылка не должна меняться.

shining-mind commented 1 week ago

Добавил тесты в PR: https://github.com/V4Fire/Client/pull/1289/commits/6cd450ca310120d4413303650363b9efcc2d49f9

При первом mounted в кэше геттера нет значения.

// first render
u14bc48a3b70499 "value" cached = false | canUseCache = false | hook = beforeDataCreate 868 ms
u14bc48a3b70499 "value" = undefined <string> 868 ms
// mounted
// the cache is invalidated due to item change: undefined -> {value: 3.14}
u14bc48a3b70499 invalidateComputedCache  "value" 871 ms
u14bc48a3b70499 "value" cached = false | canUseCache = true | hook = mounted 872 ms
u14bc48a3b70499 "value" = 3.14 <string> 872 ms

// re-render
u5a4fffc37f46c "value" cached = false | canUseCache = false | hook = beforeDataCreate 896 ms
u5a4fffc37f46c "value" = undefined <string> 896 ms
// clean memory of previous context
u14bc48a3b70499 clear "rootAttrs" 897 ms
u14bc48a3b70499 clear "sharedMods" 897 ms
u14bc48a3b70499 clear "m" 898 ms
u14bc48a3b70499 clear "db" 898 ms
u14bc48a3b70499 clear "value" 898 ms

// mounted
// the value cache is not invalidated as the item was transfered from previous context
// and didn't change: {value: 3.14} -> {value: 3.14}
u5a4fffc37f46c "value" cached = true | canUseCache = true | hook = mounted 899 ms