Введение в SWR: React Hooks для удаленной выборки данных

Опубликовано: 2022-03-10
Краткое резюме ↬ В этой статье мы рассмотрим новый способ получения данных в приложениях React под названием SWR. Это набор хуков для удаленной выборки данных, которые упрощают такие вещи, как кэширование, разбиение на страницы и т. д. Мы также будем создавать приложение Pokedex с нуля и использовать функции SWR для получения данных и их разбиения на страницы.

SWR — это облегченная библиотека, созданная Vercel (ранее ZEIT), которая позволяет извлекать, кэшировать или повторно извлекать данные в реальном времени с помощью React Hooks. Он построен с React Suspense, который позволяет вашим компонентам «ждать» чего-то, прежде чем они смогут отобразить, включая данные. SWR также поставляется с замечательными функциями, такими как зависимая выборка, фокусировка на повторной проверке, восстановление положения прокрутки и т. д. Это также очень мощный инструмент, поскольку он не зависит от серверной части и имеет хорошую поддержку TypeScript. Это пакет, который имеет светлое будущее.

Почему вас это должно волновать? Если вы ищете библиотеку, которая не только извлекает данные из API, но и позволяет выполнять такие действия, как кэширование и зависимая выборка, вам следует обратить внимание. То, что будет рассмотрено в этом руководстве, пригодится при создании приложений React с большим количеством движущихся частей. Ожидается, что вы должны были использовать Axios и Fetch API, хотя мы сравним, чем они отличаются от SWR, мы не будем вдаваться в подробности того, как они будут реализованы.

В этом руководстве я познакомлю вас с React Hooks для удаленной выборки данных, создав приложение Pokedex, которое запрашивает данные из API Pokemon. Мы также углубимся в другие функции, которые также поставляются с SWR, и выделим его отличия от популярных решений, таких как Fetch API и библиотека Axios, и объясним вам причины использования этой библиотеки и почему вы должны следить за SWR.

Итак, давайте начнем с ответа на фундаментальный вопрос: что такое КСВ?

Еще после прыжка! Продолжить чтение ниже ↓

Что такое КСВ?

SWR — это инициализм stale-while-revalidate. Это библиотека React Hooks для удаленного извлечения данных. SWR работает в три основных этапа: сначала он возвращает данные из кеша (устаревшая часть), затем отправляет запрос на выборку (часть повторной проверки) и, наконец, предоставляет актуальные данные. Но не беспокойтесь, SWR берет на себя все эти шаги за нас. Единственное, что нам нужно сделать, это дать useSWR необходимые параметры для выполнения запроса.

SWR также имеет несколько приятных функций, таких как:

  • Back-end агностик
  • Быстрая навигация по страницам
  • Повторная проверка в фокусе
  • Интервальный опрос
  • Запросить дедупликацию
  • Локальная мутация
  • Пагинация
  • TypeScript готов
  • поддержка ССР
  • Режим ожидания
  • Реагировать на нативную поддержку
  • Легкий.

Звучит волшебно? Что ж, SWR упрощает работу и повышает удобство работы с вашим приложением React. И как только мы начнем реализовывать его в нашем проекте, вы поймете, почему этот хук удобен.

Важно знать, что имя пакета — swr или SWR, а ловушка, используемая для получения функций SWR, называется useSWR .

Теоретически КСВ может быть тем, что вам нужно для улучшения выборки данных. Однако у нас уже есть два отличных способа выполнения HTTP-запросов в нашем приложении: Fetch API и библиотека Axios.

Итак, зачем использовать новую библиотеку для извлечения данных? давайте попробуем ответить на этот законный вопрос в следующем разделе.

Сравнение с Fetch и Axios

У нас уже есть много способов делать HTTP-запросы в наших приложениях React, и два самых популярных — это Fetch API и библиотека Axios. Они оба великолепны и позволяют нам легко получать или отправлять данные. Однако, как только операция будет выполнена, они не помогут нам кэшировать или разбивать данные на страницы, вам придется делать это самостоятельно.

Axios или Fetch просто обработают запрос и вернут ожидаемый ответ, не более того.

И по сравнению с SWR это немного отличается, потому что SWR под капотом использует Fetch API для запроса данных с сервера — это своего рода слой, построенный поверх него. Тем не менее, у него есть некоторые приятные функции, такие как кэширование, разбивка на страницы, восстановление положения прокрутки, зависимая выборка и т. д., а если быть точным, определенный уровень реактивности из коробки, которого нет у Axios или Fetch. Это большое преимущество, потому что наличие таких функций помогает сделать наши приложения React быстрыми и удобными для пользователя и заметно уменьшить размер нашего кода.

И в заключение просто имейте в виду, что SWR — это не то же самое, что Axios или Fetch, даже если он помогает обрабатывать HTTP-запросы. SWR более продвинут, чем они, он предоставляет некоторые улучшения для синхронизации нашего приложения с серверной частью и, следовательно, повышает производительность нашего приложения.

Теперь мы знаем, в чем отличие SWR от библиотеки Axios или Fetch API, и пришло время разобраться, зачем использовать такой инструмент.

Рекомендуемая литература : Использование REST API в React с помощью Fetch и Axios

Зачем использовать SWR для выборки данных?

Как я уже говорил ранее, SWR поставляется с некоторыми удобными функциями, которые помогают легко повысить удобство использования вашего приложения. С помощью SWR вы можете мгновенно разбивать свои данные на страницы с помощью useSWRPages , вы также можете извлекать данные, которые зависят от другого запроса, или восстанавливать позицию прокрутки, когда вы возвращаетесь на заданную страницу, и многое другое.

Обычно мы показываем пользователю сообщение о загрузке или счетчик во время получения данных с сервера. А с помощью SWR вы можете улучшить его, показывая пользователю кэшированные или устаревшие данные при получении новых данных из API. И как только эта операция будет завершена, она повторно проверит данные, чтобы показать новую версию. И вам не нужно ничего делать, SWR кэширует данные при первом их извлечении и автоматически извлекает их при новом запросе.

На данный момент мы уже видим, почему использование SWR вместо Axios или Fetch лучше, очевидно, в зависимости от того, что вы хотите создать. Но во многих случаях я рекомендую использовать SWR, потому что у него есть отличные функции, которые выходят за рамки простого извлечения и возврата данных.

Тем не менее, теперь мы можем начать создавать наше приложение React и использовать библиотеку SWR для извлечения удаленных данных.

Итак, начнем с создания нового проекта.

Настройка

Как я уже говорил во введении, мы создадим приложение, которое извлекает данные из API Pokemon. Вы также можете использовать другой API, если хотите, я пока буду придерживаться его.

И чтобы создать новое приложение, нам нужно запустить следующую команду в терминале:

 npx create-react-app react-swr

Затем нам нужно установить библиотеку SWR, сначала перейдя в папку, в которой находится приложение React.

 cd react-swr

И запустите на терминале следующую команду, чтобы установить пакет SWR.

 yarn add swr

Или, если вы используете npm:

 npm install swr

Теперь, когда мы все настроили, давайте структурируем проект следующим образом, чтобы начать использовать SWR:

 src ├── components | └── Pokemon.js ├── App.js ├── App.test.js ├── index.js ├── serviceWorker.js ├── setupTests.js ├── package.json ├── README.md ├── yarn-error.log └── yarn.lock

Как видите, структура папок проста. Единственное, на что следует обратить внимание, — это папка components , в которой находится файл Pokemon.js . Позже он будет использоваться в качестве презентационного компонента для показа одного покемона, как только мы получим данные из API.

Здорово! После этого мы можем начать извлекать данные из API с помощью useSWR .

Получение удаленных данных

Пакет SWR имеет несколько полезных функций, как мы видели выше. Однако существует два способа настройки этой библиотеки: локально или глобально.

Локальная настройка означает, что каждый раз, когда мы создаем новый файл, нам нужно снова настраивать SWR, чтобы иметь возможность получать удаленные данные. А глобальная настройка позволяет нам повторно использовать часть нашей конфигурации в разных файлах, потому что функция fetcher может быть объявлена ​​один раз и использоваться везде.

И не беспокойтесь, в этой статье мы увидим и то, и другое, а пока давайте запачкаем руки и добавим осмысленный код в файл App.js

Отображение данных

 import React from 'react' import useSWR from 'swr' import { Pokemon } from './components/Pokemon' const url = 'https://pokeapi.co/api/v2/pokemon' const fetcher = (...args) => fetch(...args).then((res) => res.json()) function App() { const { data: result, error } = useSWR(url, fetcher) if (error) return <h1>Something went wrong!</h1> if (!result) return <h1>Loading...</h1> return ( <main className='App'> <h1>Pokedex</h1> <div> {result.results.map((pokemon) => ( <Pokemon key={pokemon.name} pokemon={pokemon} /> ))} </div> </main> ) } export default App

Как видите, мы начинаем с импорта useSWR из библиотеки SWR. Это объявляет URL-адрес API, из которого вы хотите получить данные, и функцию для получения этих данных.

Здесь используется fetcher функций для преобразования данных в JSON. Он получает данные, полученные в качестве аргумента, и что-то возвращает.

Обратите внимание, что здесь я использую оператор Rest ( (...args) ), так как я не уверен в типе и длине данных, полученных в качестве параметра, поэтому я копирую все, прежде чем снова передать его в качестве аргумента для fetch . метод, предоставляемый useSWR , который преобразует данные в JSON и возвращает их.

Тем не менее, fetcher и url -адрес API теперь могут быть переданы в качестве параметров useSWR . При этом теперь он может сделать запрос и вернуть два состояния: данные получены и состояние ошибки. И data: result такой же, как data.result , мы используем деструктуризацию объекта, чтобы извлечь result из data .

Теперь с возвращенными значениями мы можем проверить, успешно ли извлечены данные, а затем пройтись по ним. И для каждого пользователя используйте компонент Pokemon для его отображения.

Теперь у нас есть данные и мы передаем их компоненту Pokemon, пришло время обновить Pokemon.js , чтобы он мог получать и отображать данные.

Создание компонента покемонов

 import React from 'react' import useSWR from 'swr' const fetcher = (...args) => fetch(...args).then((res) => res.json()) export const Pokemon = ({ pokemon }) => { const { name } = pokemon const url = 'https://pokeapi.co/api/v2/pokemon/' + name const { data, error } = useSWR(url, fetcher) if (error) return <h1>Something went wrong!</h1> if (!data) return <h1>Loading...</h1> return ( <div className='Card'> <span className='Card--id'>#{data.id}</span> <img className='Card--image' src={data.sprites.front_default} alt={name} /> <h1 className='Card--name'>{name}</h1> <span className='Card--details'> {data.types.map((poke) => poke.type.name).join(', ')} </span> </div> ) }

Здесь у нас есть компонент, который получает данные об одном покемоне из API и отображает их. Однако полученные данные не содержат всех необходимых полей, поэтому нам нужно сделать еще один запрос к API, чтобы получить полный объект Pokemon.

И, как видите, мы используем тот же процесс для получения данных, даже если на этот раз мы добавляем имя покемона к URL-адресу.

Кстати, если вы не знакомы с деструктурированием, ({ pokemon }) это то же самое, что получение реквизита и доступ к объекту покемона с помощью props.pokemon . Это просто сокращение для извлечения значений из объектов или массивов.

При этом, если вы перейдете в корневую папку проекта и запустите на терминале следующую команду:

 yarn start

Или, если вы используете npm:

 npm start

Вы должны увидеть, что данные успешно получены из API Pokemon и отображаются должным образом.

получение
Получение иллюстрации. (Большой превью)

Здорово! Теперь мы можем получать удаленные данные с помощью SWR. Однако эта настройка является локальной и может быть немного избыточной, потому что вы уже можете видеть, что App.js и Pokemon.js используют одну и ту же функцию извлечения, чтобы делать одно и то же.

Но, к счастью, в пакете есть удобный провайдер SWRConfig , который помогает настроить SWR глобально. Это компонент-оболочка, который позволяет дочерним компонентам использовать глобальную конфигурацию и, следовательно, функцию извлечения.

Чтобы настроить SWR глобально, нам нужно обновить файл index.js , потому что именно в нем компонент приложения отображается с использованием React DOM. При желании вы можете использовать SWRConfig непосредственно в файле App.js

Глобальная настройка SWR

 import React from 'react' import ReactDOM from 'react-dom' import { SWRConfig } from 'swr' import App from './App' import './index.css' const fetcher = (...args) => fetch(...args).then((res) => res.json()) ReactDOM.render( <React.StrictMode> <SWRConfig value={{ fetcher }}> <App /> </SWRConfig> </React.StrictMode>, document.getElementById('root') )

Как видите, мы начинаем с импорта SWRConfig , который является провайдером, который должен обернуть более высокий компонент или просто часть вашего приложения React, которому необходимо использовать функции SWR. Он принимает в качестве реквизита значение, которое ожидает объект конфигурации. Вы можете передать более одного свойства в объект конфигурации, здесь мне просто нужна функция для получения данных.

Теперь вместо того, чтобы fetcher функцию выборки в каждом файле, мы создаем ее здесь и передаем в качестве значения в SWRConfig . Благодаря этому теперь мы можем извлекать данные на любом уровне нашего приложения, не создавая еще одну функцию и, следовательно, избегая избыточности.

Кроме того, fetcher равен fetcher: fetcher , это просто синтаксический сахар, предложенный ES6. С этим изменением нам нужно обновить наши компоненты, чтобы использовать глобальную конфигурацию.

Использование глобальной конфигурации SWR

 import React from 'react' import useSWR from 'swr' import { Pokemon } from './components/Pokemon' const url = 'https://pokeapi.co/api/v2/pokemon' function App() { const { data: result, error } = useSWR(url) if (error) return <h1>Something went wrong!</h1> if (!result) return <h1>Loading...</h1> return ( <main className='App'> <h1>Pokedex</h1> <div> {result.results.map((pokemon) => ( <Pokemon key={pokemon.name} pokemon={pokemon} /> ))} </div> </main> ) } export default App

Теперь нам нужно только передать url -адрес в useSWR вместо передачи url - fetcher и метода получения. Давайте также немного подправим компонент Pokemon.

 import React from 'react' import useSWR from 'swr' export const Pokemon = ({ pokemon }) => { const { name } = pokemon const url = 'https://pokeapi.co/api/v2/pokemon/' + name const { data, error } = useSWR(url) if (error) return <h1>Something went wrong!</h1> if (!data) return <h1>Loading...</h1> return ( <div className='Card'> <span className='Card--id'>#{data.id}</span> <img className='Card--image' src={data.sprites.front_default} alt={name} /> <h1 className='Card--name'>{name}</h1> <span className='Card--details'> {data.types.map((poke) => poke.type.name).join(', ')} </span> </div> ) }

Вы уже можете видеть, что у нас больше нет функции выборки, благодаря глобальной конфигурации, которая передает функцию useSWR под капотом.

Теперь вы можете использовать глобальную функцию выборки в любом месте вашего приложения. Единственное, что useSWR для извлечения удаленных данных, — это URL.

Тем не менее, мы все еще можем улучшить настройку, создав собственный хук, чтобы избежать повторного объявления URL-адреса, а вместо этого просто передать путь в качестве параметра.

Расширенная настройка путем создания пользовательского хука

Для этого вам нужно создать новый файл в корне проекта с именем useRequest.js (вы можете назвать его как хотите) и добавить в него этот блок кода ниже.

 import useSwr from 'swr' const baseUrl = 'https://pokeapi.co/api/v2' export const useRequest = (path, name) => { if (!path) { throw new Error('Path is required') } const url = name ? baseUrl + path + '/' + name : baseUrl + path const { data, error } = useSwr(url) return { data, error } }

Здесь у нас есть функция, которая получает путь и, возможно, имя и добавляет его к базовому URL-адресу для создания полного URL-адреса. Затем он проверяет, получен ли параметр имени или нет, и соответственно обрабатывает его.

Затем этот URL-адрес передается в качестве параметра useSWR , чтобы иметь возможность получать удаленные данные и возвращать их. И если путь не пройден, выдает ошибку.

Здорово! теперь нам нужно немного настроить компоненты, чтобы использовать наш пользовательский хук.

 import React from 'react' import { useRequest } from './useRequest' import './styles.css' import { Pokemon } from './components/Pokemon' function App() { const { data: result, error } = useRequest('/pokemon') if (error) return <h1>Something went wrong!</h1> if (!result) return <h1>Loading...</h1> return ( <main className='App'> <h1>Pokedex</h1> <div> {result.results.map((pokemon) => ( <Pokemon key={pokemon.name} pokemon={pokemon} /> ))} </div> </main> ) } export default App

Теперь, вместо использования хука SWR, мы используем пользовательский хук, созданный поверх него, а затем передаем, как и ожидалось, путь в качестве аргумента. После этого все будет работать как раньше, но с гораздо более чистой и гибкой конфигурацией.

Давайте также обновим компонент Pokemon.

 import React from 'react' import { useRequest } from '../useRequest' export const Pokemon = ({ pokemon }) => { const { name } = pokemon const { data, error } = useRequest('/pokemon', name) if (error) return <h1>Something went wrong!</h1> if (!data) return <h1>Loading...</h1> return ( <div className='Card'> <span className='Card--id'>#{data.id}</span> <img className='Card--image' src={data.sprites.front_default} alt={name} /> <h1 className='Card--name'>{name}</h1> <span className='Card--details'> {data.types.map((poke) => poke.type.name).join(', ')} </span> </div> ) }

Вы уже видите, как наш пользовательский хук делает работу проще и гибче. Здесь нам просто нужно дополнительно передать имя покемона для получения в useRequest и он все сделает за нас.

Надеюсь, вам понравится эта классная библиотека. Тем не менее, нам еще есть что открыть, потому что SWR предлагает так много функций, и одна из них — useSWRPages , которая позволяет легко разбивать данные на страницы. Итак, давайте используем этот хук в проекте.

Разбивка наших данных на страницы с помощью useSWRPages

SWR позволяет нам легко разбивать данные на страницы и запрашивать только их часть, а при необходимости обновлять данные для отображения на следующей странице.

Теперь давайте создадим новый файл в корне проекта usePagination.js и используем его в качестве пользовательского хука для разбиения на страницы.

 import React from 'react' import useSWR, { useSWRPages } from 'swr' import { Pokemon } from './components/Pokemon' export const usePagination = (path) => { const { pages, isLoadingMore, loadMore, isReachingEnd } = useSWRPages( 'pokemon-page', ({ offset, withSWR }) => { const url = offset || `https://pokeapi.co/api/v2${path}` const { data: result, error } = withSWR(useSWR(url)) if (error) return <h1>Something went wrong!</h1> if (!result) return <h1>Loading...</h1> return result.results.map((pokemon) => ( <Pokemon key={pokemon.name} pokemon={pokemon} /> )) }, (SWR) => SWR.data.next, [] ) return { pages, isLoadingMore, loadMore, isReachingEnd } }

Как видите, здесь мы начинаем с импорта useSWRPages , который является помощником, позволяющим легко разбивать данные на страницы. Он получает 4 аргумента: ключ запроса pokemon-page , который также используется для кэширования, функцию для выборки данных, которая возвращает компонент, если данные были успешно получены, и еще одну функцию, которая берет объект SWR и запрашивает данные из следующая страница и массив зависимостей.

И как только данные извлечены, функция useSWRPages возвращает несколько значений, но здесь нам нужно 4 из них: pages , которые являются компонентом, возвращаемым с данными, функция isLoadingMore , которая проверяет, извлечены ли данные в данный момент, функция loadMore , которая помогает больше данных, и метод isReachingEnd , который определяет, есть ли еще данные для извлечения или нет.

Теперь у нас есть пользовательский хук, который возвращает необходимые значения для разбивки данных на страницы, теперь мы можем перейти к файлу App.js и немного его настроить.

 import React from 'react' import { usePagination } from './usePagination' import './styles.css' export default function App() { const { pages, isLoadingMore, loadMore, isReachingEnd } = usePagination( '/pokemon' ) return ( <main className='App'> <h1>Pokedex</h1> <div>{pages}</div> <button onClick={loadMore} disabled={isLoadingMore || isReachingEnd} > Load more... </button> </main> ) }

После импорта хука usePagination мы можем передать путь в качестве параметра и получить возвращаемые значения. А поскольку pages — это компонент, нам не нужно перебирать данные или что-то в этом роде.

Затем мы используем функцию loadMore на кнопке, чтобы получить больше данных и отключить ее, если операция извлечения не завершена или нет данных для извлечения.

Здорово! с этим изменением мы теперь можем просматривать корень проекта и запускать сервер с помощью этой команды для предварительного просмотра нашего приложения.

 yarn start

Или, если вы используете npm:

 npm start

Вы должны увидеть, что данные успешно получены, и если вы нажмете кнопку, новые данные будут получены SWR.

Пагинация
Пагинация. (Большой превью)

До сих пор мы видели на практике библиотеку SWR, и я надеюсь, что вы найдете в ней ценность. Тем не менее, у него все еще есть некоторые функции, которые можно предложить. Давайте углубимся в эти функции в следующем разделе.

Другие особенности КСВ

В библиотеке SWR есть куча полезных вещей, которые упрощают создание приложений React.

Повторная проверка фокуса

Это функция, которая позволяет обновлять или повторно проверять данные, когда вы переориентируете страницу или переключаетесь между вкладками. И по умолчанию этот функционал включен, но вы все равно можете отключить его, если он вам не подходит. Это может быть полезно, особенно если у вас есть данные с высокочастотными обновлениями.

Обновление через интервал

Библиотека SWR позволяет повторно получать данные через определенное время. Это может быть удобно, когда ваши данные изменяются с высокой скоростью или вам нужно сделать новый запрос, чтобы получить часть новой информации из вашей базы данных.

Локальная мутация

С помощью SWR вы можете установить временное локальное состояние, которое будет автоматически обновляться при получении новых данных (повторная проверка). Эта функция особенно важна, когда вы имеете дело с автономным подходом, она помогает легко обновлять данные.

Восстановление положения прокрутки

Эта функция очень удобна, особенно при работе с огромными списками. Это позволяет восстановить положение прокрутки после возвращения на страницу. И в любом случае это повышает удобство использования вашего приложения.

Зависимая выборка

SWR позволяет вам получать данные, которые зависят от других данных. Это означает, что он может извлекать данные A, и как только эта операция будет выполнена, он использует их для извлечения данных B, избегая водопадов. И эта функция помогает, когда у вас есть реляционные данные.

Тем не менее, SWR помогает улучшить взаимодействие с пользователем в любом вопросе. У него больше возможностей, и во многих случаях его лучше использовать вместо Fetch API или библиотеки Axios.

Заключение

На протяжении всей этой статьи мы видели, почему SWR — отличная библиотека. Он позволяет удаленно получать данные с помощью React Hooks и помогает упростить некоторые расширенные функции, такие как разбивка на страницы, кэширование данных, повторная выборка с интервалом, восстановление положения прокрутки и т. д. SWR также не зависит от серверной части, что означает, что он может извлекать данные из любых API или баз данных. В конечном счете, SWR значительно повышает удобство использования ваших приложений React, у него большое будущее, и вы должны следить за ним или лучше использовать его в своем следующем приложении React.

Вы можете просмотреть готовый проект вживую здесь.

Спасибо за прочтение!

Следующие шаги

Вы можете перейти по следующим ссылкам, которые дадут вам лучшее понимание, выходящее за рамки этого руководства.

  • КСВ
  • Документы SWR

Дальнейшее чтение на SmashingMag:

  • Стилизация компонентов в React
  • Лучшие редукторы с Immer
  • Компоненты высшего порядка в React
  • Создание многократно используемых компонентов React с помощью Tailwind