Когда опенсорс это часть работыНе так давно наша кодовая база на effector на рабочем проекте стала расти с бОльшей скоростью и, конечно, сильно влиять на легаси код.
Таким образом в огромных компонентах где смешаны бизнес-логика, ui-стейт, выполнение сайд-эффектов, преобразования данных, мемоизация итп, и туда стал проникать
useUnit из effector.
useUnit - хук (в случае реакта), который связывает модель со вьюшкой. Если передан стор, то он подписывает компонент на этот стор и обновляет компонент, если стор изменился. Если переданы события или эффекты, то useUnit гарантированно вызовет это событие/эффект в нужной области (scope).
useUnit умеет "батчить" апдейты - если компонент подписан на несколько сторов/событий, то он дождется всех изменений и применит из разом, но только в том случае, если используется
один useUnit.
const Component = () => {
const [user, products] = useUnit([$userStore, $productsStore])
}Если используется несколько useUnit, то магии не случится, и каждое обновление будет вызывать обновление (ререндер) компонента.
const Component = () => {
const [products] = useUnit([$productsStore])
const [user] = useUnit([$userStore])
}Очевидно – лучше меньше ререндеров, чем больше :)
Но как нам контроллировать количество вызовов этого самого
useUnit? Особенно в большой кодовой базе в больших компонентах?
Вариант А – руками.
Ходить по всей кодовой базе, менять самостоятельно вызовы, бить по рукам разработчиков, которые по каким-то причинам не соблюдают такое простое правило.
Вариант Б – заиметь статический анализатор, который укажет на проблему и попробует пофиксить её автоматически.По этому пути я и пошел - написал правило для eslint-плагина effector'a, которое парсит вызовы useUnit и мержит их, если возможно, или подсвечивает warning с ошибкой, которая сообщает, что лучше так не делать.
Хорошо, множественные useUnit устранили, но что если:
- Мы использовали в компоненте какой-то store, но теперь он нам не нужен. Мы его удалили по месту использования, но забыли удалить из useUnit
const Component = () => {
// Используем $user, но не используем $products
const [user] = useUnit([$user, $products]);
return <UserCard id={user.id} />
}В таком случае подписка сохранится - если $products поменяется, то компонент тоже обновится. Неприятно.
Для этого кейса тоже можно написать ESLint-правило! Парсим вызов
useUnit (или его алиасов, если используете), смотрим на то, что было деструктурировано и сравниваем с аргументами, которые передали.
Если аргумент есть, а деструктурированного вызова нет - сообщаем об этом пользователю.
https://github.com/effector/eslint-plugin/pull/175