Новые клиенты Next.js и SWR: что вам нужно знать

Новые клиенты 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


Оригинал