Опция initialData позволяет заранее поместить данные в
кэш TanStack Query ещё до выполнения запроса. Хук useQuery
сразу получает готовое значение и переходит в состояние успешной
загрузки (success), даже если сетевой запрос ещё не был
выполнен.
Это особенно важно в нескольких сценариях:
Пример базового использования:
import { useQuery } from '@tanstack/react-query'
function Users() {
const query = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
initialData: [
{ id: 1, name: 'Alex' },
{ id: 2, name: 'John' }
]
})
return (
<ul>
{query.data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
При первом рендере список уже будет содержать данные из
initialData.
initialDataКогда TanStack Query видит initialData, происходит
следующее:
success.data становится доступным синхронно.Главная особенность заключается в том, что initialData
считается полноценными данными кэша.
Это означает:
staleTime;initialDataСтандартный запрос проходит несколько стадий:
const query = useQuery({
queryKey: ['posts'],
queryFn: fetchPosts
})
Сначала:
status === 'pending'
isLoading === true
data === undefined
После завершения запроса:
status === 'success'
isLoading === false
data !== undefined
initialDataТеперь добавим стартовые данные:
const query = useQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
initialData: []
})
Сразу после рендера:
status === 'success'
isLoading === false
data === []
При этом TanStack Query всё ещё может отправить сетевой запрос для актуализации данных.
initialData и
фоновый refetchПо умолчанию данные из initialData считаются устаревшими
(stale).
Из-за этого после монтирования компонента TanStack Query обычно запускает refetch.
Пример:
const query = useQuery({
queryKey: ['profile'],
queryFn: fetchProfile,
initialData: {
id: 1,
name: 'Unknown'
}
})
Последовательность будет такой:
Unknown.success.staleTimeЧасто стартовые данные считаются актуальными некоторое время.
Для этого используется staleTime.
const query = useQuery({
queryKey: ['settings'],
queryFn: fetchSettings,
initialData: defaultSettings,
staleTime: 1000 * 60 * 5
})
Теперь:
initialDatainitialData может быть функцией.
Это полезно, если вычисление дорогое.
const query = useQuery({
queryKey: ['products'],
queryFn: fetchProducts,
initialData: () => {
return generateLargeDataset()
}
})
Функция вызывается только один раз при инициализации Query.
Очень распространённый сценарий — получение данных детали объекта из уже загруженного списка.
Например, имеется список пользователей:
const usersQuery = useQuery({
queryKey: ['users'],
queryFn: fetchUsers
})
Далее нужен запрос конкретного пользователя:
const userQuery = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
initialData: () => {
return queryClient
.getQueryData(['users'])
?.find(user => user.id === userId)
}
})
Преимущества:
initialDataUpdatedAtИногда важно указать время актуальности данных вручную.
Для этого существует initialDataUpdatedAt.
const query = useQuery({
queryKey: ['news'],
queryFn: fetchNews,
initialData: cachedNews,
initialDataUpdatedAt: Date.now()
})
Теперь TanStack Query понимает, что данные были обновлены только что.
Без initialDataUpdatedAt библиотека считает данные
потенциально устаревшими.
В результате может произойти немедленный refetch.
С initialDataUpdatedAt можно:
initialData и placeholderDataЭти опции часто путают.
Различие принципиальное.
initialDataplaceholderDataplaceholderDataplaceholderData используется для временного отображения
структуры данных, пока настоящий запрос ещё выполняется.
Это механизм визуального заполнения интерфейса.
Пример:
const query = useQuery({
queryKey: ['articles'],
queryFn: fetchArticles,
placeholderData: []
})
Интерфейс получает:
data === []
Но эти данные не считаются настоящими.
initialDataС placeholderData Query остаётся в состоянии
загрузки.
isPlaceholderData === true
Это специальный флаг TanStack Query.
isPlaceholderDataconst query = useQuery({
queryKey: ['products'],
queryFn: fetchProducts,
placeholderData: []
})
if (query.isPlaceholderData) {
console.log('Показываются временные данные')
}
placeholderDataПоследовательность:
Это один из самых популярных сценариев использования.
const query = useQuery({
queryKey: ['posts', page],
queryFn: () => fetchPosts(page),
placeholderData: previousData
})
Пока новая страница загружается:
keepPreviousDataРанее для подобных задач активно использовалась опция
keepPreviousData.
В новых версиях TanStack Query чаще применяется
placeholderData.
Пример:
placeholderData: previousData => previousData
placeholderData может получать предыдущие данные.
const query = useQuery({
queryKey: ['users', page],
queryFn: () => fetchUsers(page),
placeholderData: previousData => previousData
})
Это создаёт эффект непрерывного интерфейса.
Даже если placeholder отображается, запрос всё ещё считается загружаемым.
isFetching === true
Это важно для:
placeholderDataif (query.isLoading && !query.data) {
return <Skeleton />
}
После появления placeholder:
query.data
уже существует, поэтому интерфейс может отобразить старые данные вместо пустого экрана.
placeholderData
не сохраняется в кэшеЭто фундаментальное свойство.
После завершения запроса:
initialDatainitialData подходит, если:
placeholderDataplaceholderData подходит, если:
| Особенность | initialData | placeholderData |
|---|---|---|
| Записывается в кэш | Да | Нет |
| Считается настоящими данными | Да | Нет |
| Query получает success | Да | Нет |
| Может быть stale | Да | Нет |
| Используется как временный UI | Частично | Да |
| Подходит для SSR | Да | Нет |
| Подходит для пагинации | Иногда | Да |
initialDataВо время серверного рендеринга initialData особенно
полезен.
Сервер может заранее получить данные:
const dehydratedState = dehydrate(queryClient)
После этого клиент получает готовый кэш.
Преимущества:
TanStack Query поддерживает:
dehydrate;hydrate.При гидратации данные становятся аналогом initialData,
но уже на уровне всего Query Cache.
placeholderDataНекоторые разработчики ожидают, что placeholder сохранится в кэше.
Например:
placeholderData: []
Но после размонтирования компонента эти данные исчезают полностью.
initialDataОшибка возникает, когда initialData используется как
фейковые данные.
initialData: []
Если сервер должен вернуть список позже, Query уже считается успешным.
Из-за этого:
enabledconst query = useQuery({
queryKey: ['profile', userId],
queryFn: () => fetchProfile(userId),
enabled: !!userId,
placeholderData: previousData => previousData
})
Даже при изменении userId старые данные сохранятся до
завершения нового запроса.
Правильное использование initialData и
placeholderData позволяет:
initialDataИсточник истины.
Данные считаются реальными.
placeholderDataВременная визуальная прослойка.
Данные существуют только до завершения запроса.
initialDatauseQuery({
queryKey: ['user'],
queryFn: fetchUser,
initialData: cachedUser
})
Поведение:
placeholderDatauseQuery({
queryKey: ['user'],
queryFn: fetchUser,
placeholderData: cachedUser
})
Поведение: