Новые клиенты Next.js и SWR: что вам нужно знать
18 декабря 2022 г.Наш первый клиент Next.js во многом был вдохновлен SWR, но мы решили создать собственную библиотеку выборки, потому что в SWR не было нескольких важных функций, которые нам нужны.
В частности, встроенная поддержка аутентификации, мутаций (SWR ориентирован на получение данных, а не на их обновление) и улучшенная функциональность SSR.
Поначалу это нас вполне устраивало, но со временем мы поняли, что нам нужно создавать и поддерживать многие из тех функций, которые уже были у SWR. Зачем решать проблему, которая уже решена?
Мы начали экспериментировать с использованием нашего клиента TypeScript вместе с SWR при создании облачной альфа-версии.
Это оказалось отличным совпадением, наш клиент TypeScript использует fetch для выполнения запросов к WunderGraph API, который обслуживает операции GraphQL через API в стиле JSON RPC и позволяет нам использовать такие методы, как stale-while-revalidate. код>. Именно это и означает SWR.
Вскоре после этого мы выпустили нашу официальную интеграцию с SWR, чтобы мы могли начать тестирование с сообществом. Это было хорошо принято, и это значительно улучшило опыт разработчиков для наших пользователей.
Это дало нам дополнительные функции, такие как оптимистичные обновления, недействительные запросы и поддержку промежуточного программного обеспечения (подробнее об этом позже), и это значительно уменьшило сложность нашего кода.
Это вселило в нас уверенность в том, что мы можем сделать SWR нашей библиотекой выборки данных по умолчанию для React, а с выпуском SWR 2.0RC пришло время переписать наш клиент Next.js для использования SWR.
Теперь давайте углубимся в детали нового клиента Next.js. Узнайте, что изменилось и как вы можете использовать новые функции.
Этот пост относится к:
* @wundergraph/nextjs
>= 0.5.0
* @wundergraph/swr
>= 0.7.0
* swr
>= 2.0.0-rc.0
Что нового
Мы старались сохранить новый API похожим на предыдущий, но есть некоторые заметные (критические) изменения.
Сгенерированный клиент Next.js больше не создает отдельные хуки для каждой операции; вместо этого у нас есть 3 хука, useQuery
, useSubscription
и useMutation
, которые полностью типизированы.
Это делает код очень легким в производственных пакетах, но при этом использует всю мощь TypeScript.
Запросы
Вместо:
const { response, refetch } = useQuery.Weather({
input: {
forCity: 'Berlin',
},
})
Теперь вы можете написать:
const { data, error, mutate } = useQuery({
operationName: 'Weather',
input: {
forCity: 'Berlin',
},
})
Условная выборка данных теперь также возможна:
const { data: user } = useUser()
const { data, error, mutate } = useQuery({
operationName: 'ProtectedWeather',
input: {
forCity: 'Berlin',
},
enabled: !!user,
})
Обратите внимание, что refetch
был переименован в mutate
, чтобы соответствовать SWR API. Вызов mutate()
приведет к аннулированию кеша и повторной выборке запроса; вы также можете передать данные в mutate
, чтобы обновить кеш без повторной загрузки. Это полезно, например, для оптимистичных обновлений.
Больше нет хука useLiveQuery
, теперь это просто useQuery
с опцией liveQuery
, установленной на true
, что позволяет вам чтобы легко превратить любой запрос в активный запрос.
const { data, error } = useQuery({
operationName: 'Weather',
input: {
forCity: 'Berlin',
},
liveQuery: true,
})
Подписки
Подписки имеют аналогичный API. Здесь ничего существенного не изменилось, но теперь вы можете проверить, настраивается ли подписка или активна, с помощью свойств isLoading
и isSubscribed
.
const { data, error, isLoading, isSubscribed } = useSubscription({
operationName: 'Countdown',
input: {
from: 100,
},
})
Мутации
Мутации также очень похожи на предыдущий API. mutate
был переименован в trigger
, чтобы избежать путаницы с функцией mutate
из SWR.
const { data, error, trigger } = useMutation({
operationName: 'SetName',
})
// trigger is async by default
const result = await trigger({ name: 'Eelco' })
// prevent the promise from throwing
trigger({ name: 'Eelco' }, { throwOnError: false })
Что хорошего в новом API, так это то, что теперь проще сделать запросы недействительными после изменения.
Допустим, у нас есть запрос, который извлекает профиль текущего пользователя в одном компоненте, и у нас есть форма, которая обновляет профиль. Мы можем добавить обработчик onSuccess
в мутацию, которая вызывает mutate
(недействителен) в запросе GetProfile
.
const { data, error, trigger } = useMutation({
operationName: 'SetName',
})
// trigger is async by default
const result = await trigger({ name: 'Eelco' })
// prevent the promise from throwing
trigger({ name: 'Eelco' }, { throwOnError: false })
Теперь мы могли бы даже сделать это полностью оптимистичным, изменив вместо этого кеш GetProfile
, а затем повторно загрузив его; это будет выглядеть примерно так:
const Profile = () => {
const { data, error } = useQuery({
operationName: 'GetProfile',
})
return <div>{data?.getProfile.name}</div>
}
const FormComponent = () => {
const { mutate } = useSWRConfig()
const { data, error, trigger } = useMutation({
operationName: 'UpdateProfile',
onSuccess() {
// invalidate the query
mutate({
operationName: 'GetProfile',
})
},
})
const onSubmit = (event) => {
e.preventDefault();
const data = new FormData(event.target);
trigger(data, { throwOnError: false })
}
return <form onSubmit={onSubmit}><input name="name" /><button type="submit">Save></button></form>
}
ССР
Новый клиент также поддерживает SSR и даже работает с оперативными запросами и подписками.
Просто оберните свое приложение
или страницу
с помощью withWunderGraph
, и все готово.
const HomePage = () => {
const { data, error } = useQuery({
operationName: 'Weather',
input: {
forCity: 'Berlin',
},
liveQuery: true,
})
return (
<div>
<h1>Weather</h1>
<p>{data?.weather?.temperature}</p>
</div>
)
}
export default withWunderGraph(HomePage)So how does this work?
Как и в старом клиенте, мы используем react-ssr-prepass
для рендеринга дерева компонентов на сервере внутри getInitialProps
и собираем все обещания выборки из useQuery и
useSubscription
и дождитесь их разрешения.
Затем он передается клиенту, который затем передается в SWRConfig
запасной вариант. . Все это делается автоматически; вам не нужно ничего делать.
Как я объяснял во введении, SWR поддерживает промежуточное ПО, что является действительно мощной функцией и позволяет добавлять функциональные возможности к отдельным хукам. или глобально с помощью SWRConfig
.
Пакет Next.js использует промежуточное программное обеспечение для добавления поддержки SSR к хукам. Давайте немного углубимся в детали того, как это работает.
Промежуточное ПО
Наше промежуточное ПО SSR добавляется в SWRConfig
внутри с помощью withWunderGraph
; Я пройдусь по коду, чтобы объяснить, как он работает.
export const SSRMiddleWare = ((useSWRNext: SWRHook) => {
return (key: Key, fetcher: BareFetcher<Promise<unknown>> | null, config: SSRConfig) => {
// middleware logic
}
})
<SWRConfig value={{ use: [SSRMiddleWare] }}>
// your app
</SWRConfig>
Так работает логика. Во-первых, мы проверяем, вызывается ли этот хук на сервере, включен ли SSR и является ли это операцией WunderGraph. Если нет, мы просто возвращаем хук swr.
const swr = useSWRNext(key, fetcher, config)
const context = useWunderGraphContext()
const isSSR =
typeof window === 'undefined' && context?.ssr && config.ssr !== false
if (!isOperation(key) || !context || !isSSR || !key) {
return swr
}
Теперь мы сериализуем ключ, который нам нужен для ssrCache, и передаем его в SWR fallback
. Проверяем, является ли это аутентифицированной операцией; если пользователь не вошел в систему, нам не нужно выполнять операцию для SSR.
LiveQueries и подписки не имеют сборщика, поскольку данные загружаются асинхронно, но для SSR мы создаем сборщик, который будет вызывать запрос с параметром subscribeOnce
, установленным в true.
Это вернет данные подписки напрямую, а не настроит поток событий, поэтому мы сможем отображать подписки на стороне сервера ❤️.
const { operationName, input, liveQuery, subscription } = key
const _key = serialize(key)
const { ssrCache, client, user } = context
const shouldAuthenticate =
client.isAuthenticatedOperation(operationName) && !user
let ssrFetcher = fetcher
if (!ssrFetcher && (liveQuery || subscription)) {
ssrFetcher = async () => {
const result = await client.query({
operationName,
input,
subscribeOnce: true,
})
if (result.error) {
throw result.error
}
return result.data
}
}
Теперь мы устанавливаем сборщик на наш ssrCache с сериализованным ключом. Который затем считывается в getInitialProps
, возвращается клиенту и передается в параметр fallback
в SWRConfig
, а SWR + Next.js делают все остальное. .
if (ssrCache && !ssrCache[_key] && ssrFetcher && !shouldAuthenticate) {
ssrCache[_key] = ssrFetcher(key)
}
return swr
Ресурсы
Обзор
Итак, теперь мы используем SWR в качестве нашей библиотеки выборки данных по умолчанию, предоставляя вам все преимущества, такие как оптимистичные обновления при использовании WunderGraph API. Это уже значительно улучшило наш внутренний опыт разработчиков, и мы рады услышать о вашем опыте работы с новыми клиентами.
Нам также интересен ваш опыт работы с этой темой в целом.
* Используете ли вы КСВ? Что вы думаете об этом? Какие функции вам больше всего нравятся?
* Используете ли вы другие библиотеки для выборки данных и управления кешем, например React Query? (подсказка: скоро мы выпустим интеграцию с React Query).
Поделитесь этим в комментариях ниже или присоединяйтесь к нам на нашем сервере Discord.
Также опубликовано здесь
Фото Джейсона Люна на Unsplash
Оригинал