SWR обеспечивает критически важную функциональность во всех видах веб-приложений, поэтому производительность является главным приоритетом.
Встроенное кеширование и дедупликация SWR пропускают ненужные сетевые запросы,
но производительность самого хука useSWR
всё ещё имеет значение. В сложном приложении могут быть сотни
вызовов useSWR
при отрисовке одной страницы.
SWR гарантирует, что в вашем приложении:
- нет лишних запросов
- нет лишних повторных рендеров
- не импортируется ненужный код
без каких-либо изменений кода с вашей стороны.
Хуки SWR зачастую используются в приложении повторно. Например, приложение, которое рендерит аватар текущего пользователя 5 раз:
function useUser () {
return useSWR('/api/user', fetcher)
}
function Avatar () {
const { data, error } = useUser()
if (error) return <Error />
if (!data) return <Spinner />
return <img src={data.avatar_url} />
}
function App () {
return <>
<Avatar />
<Avatar />
<Avatar />
<Avatar />
<Avatar />
</>
}
Каждый компонент <Avatar>
имеет внутри хук useSWR
. Поскольку они имеют одинаковый ключ SWR
и рендерятся почти одновременно, будет выполнен только 1 сетевой запрос.
Вы можете повторно использовать свои хуки данных (например, useUser
в приведенном выше примере) повсюду,
не беспокоясь о производительности или дублировании запросов.
Существует также опция dedupingInterval
для переопределения интервала дедупликации
по умолчанию.
SWR глубоко сравнивает изменения данных по умолчанию (with some limitations - for objects where the constructor is not Object
like Sets and Maps, referential equality is used. See stable-hash for more details). Если значение data
не изменилось,
повторный рендеринг запускаться не будет.
Вы также можете настроить функцию сравнения с помощью опции compare
, если хотите
изменить поведение. Например, некоторые ответы API возвращают отметку времени сервера, которую вы,
возможно, захотите исключить из сравнения данных.
useSWR
возвращает 3 значения с сохранением состояния: data
, error
и isValidating
,
каждое из которых может обновляться независимо. Например, если мы выведим эти значения внутри
полного жизненного цикла выборки данных, это будет примерно так:
function App () {
const { data, error, isValidating } = useSWR('/api', fetcher)
console.log(data, error, isValidating)
return null
}
В худшем случае (первый запрос не удался, затем повторная попытка была успешной) вы увидите 4 строки журнала:
// console.log(data, error, isValidating)
undefined undefined true // => начало выборки
undefined Error false // => конец выборки, получили ошибку
undefined Error true // => начало повторной попытки
Data undefined false // => конец повторной попытки, получение данных
Изменения состояния имеют смысл. Но это также означает, что наш компонент рендерился 4 раза.
Если мы изменим наш компонент на использование только data
:
function App () {
const { data } = useSWR('/api', fetcher)
console.log(data)
return null
}
Волшебство происходит — теперь всего 2 рендера:
// console.log(data)
undefined // => гидратация / начальный рендер
Data // => конец повторной попытки, получение данных
Точно такой же процесс произошёл внутри, возникла ошибка при первом запросе, затем мы получили данные
при повторной попытке. Однако SWR обновляет только состояния, которые используются компонентом,
которым сейчас является только data
.
Если вы не всегда используете все эти 3 состояния, вы уже извлекаете пользу из этого функционала. В Vercel эта оптимизация приводит к сокращению повторного рендеринга примерно на 60%.
Пакет SWR легко встряхивается и не имеет побочных эффектов
(side-effects). Это означает, что если вы импортируете только основной API useSWR
, неиспользуемые API,
такие как useSWRInfinite
, не будут включены в ваше приложение.