Как можно закэшировать данные useSWR, чтобы при возврате на страницу данные брались из кэша
Что проверяют
Понимаете ли вы, как устроен кэш SWR и почему данные при возврате на страницу и так уже берутся из него. Настоящий вопрос обычно в том, как убрать лишний повторный запрос или сохранить кэш после перезагрузки вкладки.
Как отвечать
Начните с поведения по умолчанию. У SWR есть глобальный кэш в памяти (по сути Map), ключом служит первый аргумент useSWR. Кэш не очищается при размонтировании компонента, поэтому при возврате на страницу с тем же ключом data доступна сразу, без спиннера. Затем SWR делает то, что заложено в названии stale-while-revalidate: показывает старые данные и в фоне перезапрашивает свежие. Флаг isLoading при этом остаётся false, а isValidating становится true.
Дальше варианты под задачу:
- не перезапрашивать при монтировании:
revalidateIfStale: false, если данные в кэше уже есть, запроса не будет; - данные, которые не меняются:
useSWRImmutable, он отключаетrevalidateIfStale,revalidateOnFocusиrevalidateOnReconnectразом; - дедупликация:
dedupingInterval(по умолчанию 2 секунды) склеивает одинаковые запросы из разных компонентов; - кэш между перезагрузками: провайдер в
SWRConfig, напримерprovider: () => new Map(JSON.parse(localStorage.getItem("swr") ?? "[]"))с сохранением вbeforeunload; - после изменений:
mutate(key)илиmutate(key, newData, { revalidate: false }), чтобы кэш не показывал устаревшее.
Хорошо упомянуть стабильность ключа: /api/items?page=1 и /api/items?page=1& это разные записи кэша.
Где спотыкаются
Отвечают revalidateOnFocus: false. Он про возврат фокуса на вкладку, а переход между страницами в SPA это новое монтирование, за него отвечает revalidateIfStale.
Путают кэширование с refreshInterval. Опрос по таймеру делает запросов больше, а не меньше.
Ожидают, что кэш переживёт перезагрузку страницы. По умолчанию он живёт только в памяти текущей вкладки, для сохранения нужен свой провайдер, а в localStorage не стоит класть персональные данные.