Разработка сайтов в Лисичанске, ЛНР. React Query 3: руководство по выборке данных и управлению ими

 
 

Создание интерфейсных CRUD-приложений часто начинается с простого, а затем усложняется по мере того, как вы продолжаете добавлять функции. Для каждой конечной точки API вам необходимо иметь дело с управлением состоянием, синхронизацией, кэшированием и обработкой ошибок. В этой статье вы узнаете о библиотеке под названием React Query и о том, как она может помочь решить все эти проблемы. Библиотека описывает себя как «отсутствующую библиотеку для извлечения данных», обеспечивающую «управление состоянием сервера» для React.

Мы будем использовать полный демонстрационный проект React Query, чтобы узнать об основных функциях, предоставляемых библиотекой. Затем вы сможете применить эти знания в своих собственных проектах. Во-первых, давайте ознакомимся с рядом пунктов, прежде чем приступить к настройке проекта.

О Реагирующем Запросе 3

React Query — проект с открытым исходным кодом, созданный Таннером Линси. Последняя основная версия React Query 3 была официально выпущена в декабре 2020 года. В этой новой версии были добавлены новые функции и улучшены существующие.

Вы должны знать, что в React Query 2.x, который был довольно популярен до выхода новой версии, есть ряд критических изменений. Существует руководство по миграции, в котором четко объясняются эти изменения, так как вы, вероятно, столкнетесь с множеством устаревших руководств, написанных для более старой версии.

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

Предпосылки

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

Реагировать

Реактивный маршрутизатор

Реагировать на крючки

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

В среде вашего разработчика вам необходимо настроить следующее:

Node.js

идти

Клиент REST, такой как Postman, Insomnia или расширение REST VS Code.

С этим покончено, давайте приступим к настройке демонстрационного проекта.

О проекте

Демонстрационный проект, который мы будем анализировать, представляет собой внешнее приложение React, которое отображает данные, предоставленные сервером REST JSON API. Приложение состоит всего из пяти страниц, демонстрирующих функции React Query, о которых мы узнаем. Эти функции включают в себя:

Основной запрос

Постраничный запрос

Бесконечный запрос

Создать мутацию

Обновить мутацию

Удалить мутацию

React Query предоставляет гораздо больше возможностей, которые, к сожалению, выходят за рамки этой статьи. Ниже приведен предварительный просмотр приложения, с которым мы будем работать.

предварительный просмотр запроса реакции

Настройка проекта

Перед тем, как мы приступим к настройке, я думаю, лучше всего кратко ознакомиться с дополнительными зависимостями, используемыми в проекте. Это включает:

Vite: очень быстрый инструмент сборки

WindiCSS: очень быстрый CSS-компилятор Tailwind

React Hook Form: конструктор форм и библиотека проверки, использующая хуки React.

React Modal: доступный модальный компонент

Axios: HTTP-клиент на основе обещаний для браузеров

JSON Server: полный поддельный сервер REST API.

Чтобы настроить демонстрационное приложение React Query на вашем компьютере, выполните следующие инструкции:

# Clone the project

git clone git@github.com:sitepoint-editors/react-query-demo.git /* place application containers/views here */ ); } export default App; Любой дочерний компонент QueryClientProviderсможет получить доступ к хукам, предоставляемым библиотекой React Query. Крючки, которые мы будем использовать в этой статье: useQuery useInfiniteQuery useMutation useQueryClient Вот обновленная (упрощенная) версия App.jsxдочерних представлений, которые мы будем использовать: import { QueryClient, QueryClientProvider } from "react-query"; import BasicQuery from "./views/BasicQuery"; import InfiniteQuery from "./views/InfiniteQuery"; import PaginatedQuery from "./views/PaginatedQuery"; import CreateUser from "./views/CreateUser"; import EditUser from "./views/EditUser"; function App() { const queryClient = new QueryClient(); return ( ); } export default App; Компоненты пользовательского интерфейса Прежде чем мы перейдем к следующему разделу, я думаю, лучше всего иметь обзор основных компонентов пользовательского интерфейса, используемых в проекте для отображения, создания и обновления пользовательских данных. Мы начнем с components/UserTable.jsx. BasicQuery.jsxЭтот табличный компонент отображает пользовательские данные и используется PaginatedQuery.jsxстраницами. Для этого требуется одно свойство, массив пользователей . Ниже приведена урезанная версия готового файла: import React, { useState, useContext } from "react"; import { Link } from "react-router-dom"; import EditIcon from "../icons/edit"; import DeleteIcon from "../icons/delete"; function UserTable({ users }) { const rows = users.map((user, index) => ( {user.id} {user.first_name} {user.last_name} {user.email} {user.gender}

)); return (
 

Create User {rows}

Id First Name Last Name Email Gender Action

); } Далее мы рассмотрим components/UserForm.jsx. Этот компонент формы используется страницами views/CreateUser.jsxи views/EditUser.jsxдля выполнения своих задач. Ниже представлена ​​упрощенная версия компонента: import React from "react"; import { useForm } from "react-hook-form"; import { useHistory } from "react-router-dom"; import "./form.css"; function UserForm({ user, submitText, submitAction }) { const { register, formState: { errors }, handleSubmit, } = useForm({ defaultValues: user || {}, }); const history = useHistory(); return (

{user && (
)}
{errors.first_name && "First name is required"}
{errors.last_name && "Last name is required"}
{errors.email && errors.email.type === «required» &&

 

«Email is required»}

{errors.email &&

errors.email.type === «pattern» &&

«Provide a valid email address»}

 

 

 

 

 

 

{errors.gender && «Gender is required»}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

) ;

 

}

 

export default UserForm;

 

Компонент UserFormпредназначен для проверки представленных пользовательских данных. Ожидается следующий реквизит:

 

user: объект данных (необязательно)

 

submitText: текстовое значение для кнопки «Отправить «.

 

submitAction: функция обработки отправки формы

 

Форма пользователя

 

В следующем разделе мы рассмотрим основные функции React Query.

 

Основной запрос

 

Получение данных с помощью React Query довольно просто. Все, что вам нужно сделать, это определить функцию выборки, а затем передать ее в качестве параметра useQueryмутации. Вы можете увидеть пример views/BasicQuery.jsxстраницы ниже:

 

import React from «react»;

 

import { useQuery } from «react-query»;

 

import UserTable from «.../components/UserTable»;

 

function BasicQuery () {

 

const fetchAllUsers = async () =>

 

await (await fetch («http: //localhost:3004/users»)).json () ;

 

const { data, error, status } = useQuery («users», fetchAllUsers) ;

 

return (

 

 

 

Basic Query Example

 

 

{status === «error» &&

{error.message}
}

 

{status === «loading» &&

Loading...
}

 

{status === «success» && }

 

 

 

 

 

) ;

 

}

 

export default BasicQuery;

 

Давайте разберем это:

 

Во- первых, мы импортируем useQueryчерез оператор import { useQuery } from «react-query».

 

Затем мы объявляем функцию обещания — fetchAllUsers— которая извлекает данные с нашего поддельного сервера JSON API.

 

Затем мы инициируем useQueryфункцию ловушки. Требуются следующие параметры:

 

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

 

функция запроса, которая должна возвращать обещание, которое либо разрешит данные, либо выдаст ошибку.

 

Функция useQueryвозвращает следующие переменные состояния:

 

data: это результат функции fetch (promise).

 

error: если выдается ошибка, это будет установлено. В противном случае это null, если запрос на выборку выполнен успешно.

 

status: это строка, которая может иметь значение idle, или.loadingerrorsuccess

 

Хук useQueryпринимает гораздо больше параметров и возвращает гораздо больше переменных, которые были задокументированы в документации React Query. Приведенный выше пример предназначен для демонстрации минимальной настройки, необходимой для выполнения запроса API с использованием библиотеки.

 

Также обратите внимание на statusреактивность переменной. Изначально установлено значение loading. Затем, когда запрос выполнен успешно, для него устанавливается значение success, в результате чего React повторно отображает компонент и обновляет пользовательский интерфейс.

 

Запрос одной записи

 

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

 

вам нужно передать аргумент функции выборки через анонимную функцию

 

вам нужно уникальное имя запроса для каждой отдельной записи, что вы можете сделать с помощью массива:[queryName, {params}]

 

function () {

 

const fetchUser = async (id) =>

 

await (await fetch (`http: //localhost:3004/users/${id}`)).json () ;

 

const { data, error, status } = useQuery ([«user», { id }], (id) =>

 

fetchUser (id)

 

) ;

 

return (...)

 

}

 

Однако есть альтернативный способ передачи аргументов. Рассмотрим следующий код:

 

const { data, error, status } = useQuery ([«user», { id }], fetchUser) ;

 

Используя приведенный выше синтаксис, вам нужно изменить fetchUserфункцию, чтобы она принимала queryKeyобъект следующим образом:

 

const fetchUser = async ({ queryKey }) => {

 

const [_key, { id }] = queryKey;

 

const response = await fetch (`http: //localhost:3004/users/${id}`) ;

 

if (! response.ok) {

 

throw new Error (response.statusText) ;

 

}

 

return response.json () ;

 

};

 

Поскольку мы используем Fetch API, ответы 404 не считаются ошибками. Вот почему нам нужно написать дополнительную логику для обработки этой ситуации. Выполнение этой дополнительной проверки не требуется при использовании клиентской библиотеки Axios API.

 

Посмотрите, views/EditUser.jsxкак реализован весь код. Там есть код мутации, который мы обсудим позже в этой статье.

 

Инструменты разработчика

 

Отладку кода React Query можно легко выполнить с помощью Devtools. Это утилита, которая визуализирует внутреннюю работу React Query в режиме реального времени по мере выполнения кода вашего приложения. Настройка его следующая:

 

import { ReactQueryDevtools } from «react-query/devtools»;

 

function App () {

 

return (

 

 

{/* The rest of your application */}

 

 

 

 

) ;

 

}

 

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

 

Панель инструментов разработчика

 

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

 

Конфигурация

 

В приложении React Query, когда страница загружается в первый раз, библиотека извлекает данные из API, представляет их вам, а затем кэширует. Когда это произойдет, вы увидите сообщение о загрузке.

 

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

 

Кэширование позволяет вашему внешнему приложению работать быстро, особенно если у вас медленный сервер API. Однако может возникнуть ситуация, когда пользователи могут начать работать с устаревшими данными. В React Query это называется устаревшими данными.

 

Есть несколько параметров конфигурации, которые могут помочь вам оптимизировать ваше приложение для повышения производительности или надежности:

 

cacheTime: по умолчанию 5 минут или 300000 миллисекунд.

 

staleTime: по умолчанию 0 миллисекунд

 

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

 

Увеличение staleTimeзначения может повысить производительность приложения, если вы знаете, что вероятность обновления получаемых данных мала. Вы можете определить эти настройки, передав хуку третий аргумент useQuery:

 

function Todos () {

 

const result = useQuery ('todos’, () => fetch ('/todos’), {

 

staleTime: 60 * 1000 // 1 minute

 

cacheTime: 60 * 1000 * 10 // 10 minutes

 

})

 

}

 

Вы также можете установить Infinityдля любого свойства. Это отключит сборку мусора для cacheTimeи сделает данные никогда не устаревшими для staleTime.

 

Постраничные запросы

 

В примере с базовым запросом все 250 записей были загружены одновременно. Более удобным для пользователя подходом является разбиение данных на страницы. Мы можем добиться этого с помощью useQueryхука. В предыдущих версиях React Query это делалось с помощью usePaginateQueryхука, которого больше нет в React Query 3.

 

Реализация пагинации фактически начинается с внутреннего API-сервера. К счастью для нас, json-serverесть поддержка разбиения на страницы. Чтобы получить доступ к этой функции, вам необходимо добавить следующие параметры к URL-адресу конечной точки:

 

_page: номер страницы

 

_limit: количество записей на странице

 

Пример: http: //localhost:3004/users? _page=5&_limit=10.

 

Давайте теперь посмотрим, как достигается пагинация с помощью useQueryхука:

 

import React, { useState } from «react»;

 

import { useQuery } from «react-query»;

 

const pageLimit = 15;

 

const fetchUsers = async (page = 1) =>

 

await (

 

await fetch (`http: //localhost:3004/users? _page=${page}&_limit=${pageLimit}`)

 

).json () ;

 

function Users () {

 

const [page, setPage] = useState (1) ;

 

const { data } = useQuery ([«paginatedUsers», page], () => fetchUsers (page), {

 

keepPreviousData: true,

 

}) ;

 

}

 

Этот пример очень похож на базовый запрос, который мы рассматривали ранее. Однако есть несколько ключевых отличий:

 

Функция обещания fetchUsersтеперь принимает целочисленный pageпараметр. Размер страницы задается с помощью переменной pageLimit.

 

Подпись useQueryхука выглядит совсем иначе:

 

Первый параметр — это массив, [«paginatedUsers», page]. Это нужно для того, чтобы отслеживать данные каждой страницы отдельно.

 

Второй параметр — анонимная функция. Он определен таким образом, чтобы передать pageаргумент fetchUsersфункции.

 

Третий аргумент — это конфигурация объекта, в которую мы можем передать несколько настроек. В этом случае установка keepPreviousDataзначения true информирует React Query о кэшировании ранее извлеченных данных. По умолчанию этот параметр равен false, что приводит к обновлению предыдущих просмотренных страниц.

 

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

 

import { useQuery, useQueryClient } from «react-query»;

 

function Example () {

 

const queryClient = useQueryClient () ;

 

const [page, setPage] = React.useState (0) ;

 

// Prefetch the next page!

 

React.useEffect (() => {

 

if (data?.hasMore) {

 

queryClient.prefetchQuery ([«paginatedUsers», page + 1], () =>

 

fetchUsers (page + 1)

 

) ;

 

}

 

}, [data, page, queryClient]) ;

 

}

 

Обратите внимание, что data.hasMoreэто свойство API сервера. К сожалению, наш поддельный API-сервер этого не поддерживает. При использовании реальной серверной части API вы, вероятно, получите ответ, который выглядит примерно так:

 

{

 

«items»: [

 

{

 

«lives»: 9,

 

«type»: «tabby»,

 

«name»: «Bobby»

 

},

 

{

 

«lives»: 2,

 

«type»: «Ginger»,

 

«name»: «Garfield»

 

},

 

...

 

],

 

«meta»: {

 

«itemCount»: 10,

 

«totalItems»: 20,

 

«itemsPerPage»: 10,

 

«totalPages»: 5,

 

«currentPage»: 2

 

},

 

«links»: {

 

«first»: «http: //cats.com/cats? limit=10»,

 

«previous»: «http: //cats.com/cats? page=1&limit=10»,

 

«next»: «http: //cats.com/cats? page=3&limit=10»,

 

«last»: «http: //cats.com/cats? page=5&limit=10»

 

}

 

}

 

Обратите внимание, что в структуре тела ответа есть дополнительные метаданные, которые могут помочь проверить кнопки разбиения на страницы. При json-serverвыполнении запроса с разбивкой на страницы мы получаем следующий результат:

 

HTTP/1.1 200 OK

 

X-Powered-By: Express

 

Vary: Origin, Accept-Encoding

 

Access-Control-Allow-Credentials: true

 

Cache-Control: no-cache

 

Pragma: no-cache

 

Expires: -1

 

X-Total-Count: 250

 

Access-Control-Expose-Headers: X-Total-Count, Link

 

Link: ; rel="first", ; rel="prev", ; rel="next", ; rel="last"

 

X-Content-Type-Options: nosniff

 

Content-Type: application/json; charset=utf-8

 

ETag: W/«567-FwlexqEes6H/+Xt0qULv2G4aUN4»

 

Content-Encoding: gzip

 

Date: Thu, 29 Apr 2021 15:24:58 GMT

 

Connection: close

 

Transfer-Encoding: chunked

 

[

 

{

 

«id»: 42,

 

«first_name»: «Whitby»,

 

«last_name»: «Damrell»,

 

«email»: «wdamrell15@i2i.jp»,

 

«gender»: «Female»

 

},

 

{

 

«id»: 43,

 

«first_name»: «Fairleigh»,

 

«last_name»: «Staner»,

 

«email»: «fstaner16@tripod.com»,

 

«gender»: «Female»

 

},

 

...

 

]

 

Обратите внимание на атрибут Link, представленный в разделе заголовка. Мы можем использовать эту информацию, чтобы написать лучший код разбиения на страницы. К сожалению, данные не в формате, который можно легко использовать с кодом JavaScript. Мы рассмотрим, как справиться с этим в следующем разделе. Сейчас мы просто используем простую проверку, чтобы определить, достигли ли мы последней страницы.

 

Ниже представлена ​​урезанная версия последней views/PaginatedQuery.jsxстраницы:

 

import React, { useState } from «react»;

 

import { useQuery } from «react-query»;

 

import UserTable from «.../components/UserTable»;

 

const pageLimit = 15;

 

const fetchUsers = async (page = 1) => {

 

const response = await fetch (

 

`http: //localhost:3004/users? _page=${page}&_limit=${pageLimit}`

 

) ;

 

return response.json () ;

 

};

 

function PaginatedQuery () {

 

const [page, setPage] = useState (1) ;

 

const { data, isLoading, isError, status, error } = useQuery (

 

[«paginatedUsers», page],

 

() => fetchUsers (page),

 

{

 

keepPreviousData: true,

 

}

 

) ;

 

const prevPage = () => {

 

if (page > 1) setPage (page — 1) ;

 

};

 

const nextPage = () => {

 

setPage (page + 1) ;

 

};

 

return (

 

 

 

Paginated Query Example

 

 

{isError &&

{error.message}
}

 

{isLoading &&

Loading...
}

 

{status === «success» && }

 

 

{/* start of pagination buttons */}

 

 

 

 

Page: {page}

<button onClick={nextPage} disabled={data && data.length < pageLimit}>

Next

 

 

 

{/* end of pagination buttons */}

 

 

 

) ;

 

}

 

export default PaginatedQuery;

 

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

 

Ниже скриншот PaginatedQueryстраницы.

 

Запрос с разбивкой на страницы

 

Бесконечные запросы

 

До сих пор мы использовали useQueryхук только для управления получением данных из нашего внутреннего API. В этом разделе вы узнаете, как реализовать функцию «бесконечной прокрутки». Пользователям потребуется нажать кнопку «Загрузить еще «, чтобы вызвать выборку данных.

 

Для этого мы будем использовать useInfiniteQueryхук, который очень похож на useQueryхук, но имеет несколько ключевых отличий. Во-первых, вам понадобится внутренний API, поддерживающий разбиение курсора на страницы:

 

fetch («/api/projects? cursor=0») ;

 

К сожалению, наш json-serverбэкэнд этого не делает. Для наших целей мы реализуем обходной путь, используя существующую поддержку нумерации страниц, чтобы заставить работать бесконечные запросы. Давайте посмотрим, как мы определяем нашу fetchUsersфункцию:

 

const pageLimit = 5;

 

const fetchUsers = ({ pageParam = 1 }) =>

 

axios.get (

 

`http: //localhost:3004/users? _page=${pageParam}&_limit=${pageLimit}`

 

) ;

 

Функция fetchUsersаналогична PaginatedQueryверсии, за исключением того, что мы возвращаем полный Responseобъект вместо разрешенного массива данных. Мы сделали это, чтобы иметь доступ к Linkобъекту, представленному в заголовке:

 

Link: ; rel="first",

 

; rel="next",

 

; rel="last"

 

Заголовок Linkвозвращает строку, содержащую метаданные о текущей позиции страницы. При использовании Axios мы можем получить доступ к вышеуказанной информации, используя файлы response.headers.link. При использовании Fetch API для выполнения запроса используйте response.headers.get ('Link’) для доступа к нему.

 

Затем нам нужно преобразовать Linkметаданные в формат, к которому мы можем легко получить доступ в коде. Мы можем выполнить преобразование, используя эту функцию, описанную в статье Джоша Франка:

 

const parseLinkHeader = (linkHeader) => {

 

const linkHeadersArray = linkHeader

 

.split («,»)

 

.map ((header) => header.split («;»));

 

const linkHeadersMap = linkHeadersArray.map ((header) => {

 

const thisHeaderRel = header[1].replace (/"/g, «»).replace («rel=", "») ;

 

const thisHeaderUrl = header[0].slice (1, -1) ;

 

return [thisHeaderRel, thisHeaderUrl];

 

}) ;

 

return Object.fromEntries (linkHeadersMap) ;

 

};

 

Когда мы передаем Linkстроку заголовка в функцию, мы получаем следующий объект JavaScript:

 

{

 

first: «http: //localhost:3004/users? _page=1&_limit=5»,

 

next: «http: //localhost:3004/users? _page=2&_limit=5»,

 

last: «http: //localhost:3004/users? _page=50&_limit=5»

 

}

 

Теперь мы можем извлечь значение для следующей страницы с помощью функции URLSearch. Вам нужно будет указать частичный URL-адрес в формате? _page=2&_limit=5, чтобы он работал. Вот фрагмент кода, где мы извлекаем nextPageзначение:

 

const nextPageUrl = parseLinkHeader (response.headers.link)["next"];

 

// split URL string

 

const queryString = nextPageUrl.substring (

 

nextPageUrl.indexOf («?»),

 

nextPageUrl.length

 

) ; // returns '? _page=2&_limit=5'

 

const urlParams = new URLSearchParams (queryString) ;

 

const nextPage = urlParams.get («_page») ; // returns 2

 

Используя код, который мы определили до сих пор, у нас теперь есть обходной путь функции «курсора» для нашего поддельного API-интерфейса. Вам, вероятно, будет легче с реальной серверной частью API, которая поддерживает разбивку курсора на страницы. Имея эту логику, вот как мы можем определить наш useInfiniteQuery:

 

const {

 

data,

 

error,

 

fetchNextPage,

 

hasNextPage,

 

isFetchingNextPage,

 

status,

 

} = useInfiniteQuery («infiniteUsers», fetchUsers, {

 

getNextPageParam: (lastPage) => {

 

// The following code block is specific to json-server api

 

const nextPageUrl = parseLinkHeader (lastPage.headers.link)["next"];

 

if (nextPageUrl) {

 

const queryString = nextPageUrl.substring (

 

nextPageUrl.indexOf («?»),

 

nextPageUrl.length

 

) ;

 

const urlParams = new URLSearchParams (queryString) ;

 

const nextPage = urlParams.get («_page») ;

 

return nextPage;

 

} else {

 

return undefined;

 

}

 

},

 

}) ;

 

Приведенный выше фрагмент кода выглядит сложным, поэтому позвольте мне пояснить useInfiniteQueryдля вас синтаксис:

 

const {... } = useInfiniteQuery (queryKey, queryFn, {...options})

 

Мы должны предоставить только три аргумента:

 

Первый аргумент — это queryKey.

 

Второй аргумент — queryFn— это функция обещания, которая извлекает данные с разбивкой на страницы курсора.

 

Третий аргумент — это объект конфигурации JavaScript, в котором вы определяете такие параметры, как staleTimeи cacheTime.

 

В случае useInfiniteQuery, вы должны предоставить функцию, вызываемую getNextPageParamдля работы кнопки бесконечной прокрутки. Эта функция определяет следующую страницу для загрузки. Он имеет следующий синтаксис:

 

{

 

getNextPageParam: (lastPage, allPages) => {

 

// lastPage: the last page (in our case last `Response` object) fetched by `fetchUsers` function

 

// allPages: List of all pages that have already been fetched

 

// return int|undefined: return `nextPage` as integer. Return `undefined` when there are no more pages

 

};

 

}

 

Прочтите комментарии, чтобы понять назначение входных и выходных данных функции. Давайте теперь посмотрим на дополнительные состояния, которые возвращает хук:

 

data: возвращает массив страниц, data.pages[]

 

fetchNextPage: когда эта функция выполняется, она загружает следующую страницу, полагаясь на работу getNextPageParamфункции

 

hasNextPage: возвращает true, если есть следующая страница

 

isFetchingNextPage: возвращает true при загрузке следующей страницы с помощьюfetchNextPage

 

Ниже приведен фрагмент того, как возвращаемые состояния используются для определения нашей Load moreкнопки:

 

<button

 

onClick={ () => fetchNextPage () }

 

disabled={! hasNextPage || isFetchingNextPage}

 

>

 

Load More...

 

 

 

В data.pages[]массиве каждый pageэлемент представляет собой массив, содержащий записи данных. Каждый раз, когда пользователь нажимает кнопку «Загрузить еще «, к массиву pageдобавляется новый элемент. data.pages[]Нам нужно определить новую функцию для извлечения записей из этой вложенной структуры. Обратите внимание, что в этом случае каждый pageявляется Responseобъектом Axios, поэтому нам нужно указать page.dataдоступ к каждой пользовательской записи.

Ниже приведен фрагмент кода, который мы будем использовать для сопоставления каждого пользователя с

  • тегом:

    userList = data.pages.map ((page, index) => (

    {page.data.map ((user) => (

     

  •  

    {user.id}. {user.first_name} {user.last_name}

))}

 

 

 

));

 

К настоящему моменту у вас должно быть базовое понимание того, как использовать useInfiniteQueryхук. Давайте теперь посмотрим, как все views/InfiniteQuery.jsxвыглядит:

 

import React from «react»;

 

import { useInfiniteQuery } from «react-query»;

 

import axios from «axios»;

 

function InfiniteQuery () {

 

const pageLimit = 5;

 

const fetchUsers = ({ pageParam = 1 }) =>

 

axios.get (

 

`http: //localhost:3004/users? _page=${pageParam}&_limit=${pageLimit}`

 

) ;

 

const parseLinkHeader = (linkHeader) => {

 

const linkHeadersArray = linkHeader

 

.split («,»)

 

.map ((header) => header.split («;»));

 

const linkHeadersMap = linkHeadersArray.map ((header) => {

 

const thisHeaderRel = header[1].replace (/"/g, «»).replace («rel=", "») ;

 

const thisHeaderUrl = header[0].slice (1, -1) ;

 

return [thisHeaderRel, thisHeaderUrl];

 

}) ;

 

return Object.fromEntries (linkHeadersMap) ;

 

};

 

const {

 

data,

 

error,

 

fetchNextPage,

 

hasNextPage,

 

isFetchingNextPage,

 

status,

 

} = useInfiniteQuery («infiniteUsers», fetchUsers, {

 

getNextPageParam: (lastPage) => {

 

// The following code block is specific to json-server api

 

const nextPageUrl = parseLinkHeader (lastPage.headers.link)["next"];

 

if (nextPageUrl) {

 

const queryString = nextPageUrl.substring (

 

nextPageUrl.indexOf («?»),

 

nextPageUrl.length

 

) ;

 

const urlParams = new URLSearchParams (queryString) ;

 

const nextPage = urlParams.get («_page») ;

 

return nextPage;

 

} else {

 

return undefined;

 

}

 

},

 

}) ;

 

let userList;

 

if (data) {

 

userList = data.pages.map ((page, index) => (

 

 

{page.data.map ((user) => (

 

 

  •  

    {user.id}. {user.first_name} {user.last_name}

))}

 

 

 

));

 

}

 

return (

 

 

 

Infinite Query

 

 

{error &&

An error occurred: {error.message}
}

 

{isFetchingNextPage &&

Fetching Next Page...
}

 

{status === «success» &&
  • {userList}
}

 

 

 

 

<button

onClick={ () => fetchNextPage () }

disabled={! hasNextPage || isFetchingNextPage}

>

Load More...

 

 

 

 

 

 

) ;

 

}

 

export default InfiniteQuery;

 

Надеюсь, завершенный код уже должен иметь смысл, поскольку все разделы были объяснены. Ниже приведен скриншот страницы «Пример бесконечного запроса». Я сократил количество db.jsonпользователей до 13, чтобы продемонстрировать следующие результаты:

 

Бесконечная страница запроса

 

Обратите внимание, что кнопка «Загрузить еще «неактивна, так как мы достигли последней страницы. Это знаменует собой конец нашего исследования обработчиков запросов. Давайте посмотрим, как мы можем добиться функциональности CRUD с помощью библиотеки React Query.

 

Мутации

 

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

 

Для useMutationхука требуется только функция обещания, которая будет отправлять данные во внутренний API. Он вернет следующие состояния:

 

isLoading: возвращает true во время выполнения асинхронной операции.

 

isError: возвращает true, если произошла ошибка

 

error: возвращает объект ошибки, если он присутствует

 

isSuccess: возвращает true после успешной мутации

 

Чтобы выполнить фактическое действие мутации, все, что вам нужно сделать, это выполнить mutation.mutate (data). Вы можете заключить его как функцию и назначить событие нажатия кнопки.

 

Ниже приведен снимок views/CreateUser.jsxстраницы. Вы можете увидеть, как каждая переменная состояния использовалась для рендеринга различных элементов пользовательского интерфейса.

 

import { useMutation } from «react-query»;

 

import axios from «axios»;

 

import { Redirect } from «react-router-dom»;

 

import UserForm from «.../components/UserForm»;

 

const postUser = async (newUser) =>

 

await (await axios.post («http: //localhost:3004/users», newUser)).data;

 

function CreateUser () {

 

const mutation = useMutation ((newUser) => postUser (newUser));

 

const { isLoading, isError, error, isSuccess } = mutation;

 

const onSubmit = async (data) => {

 

mutation.mutate (data) ;

 

};

 

if (isSuccess) {

 

return ;

 

}

 

return (

 

 

 

New User

{isError &&

An error occurred: {error.message}
}

 

{isLoading &&

Loading...
}

 

 

 

 

) ;

 

}

 

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

 

Пример мутации обновления:

 

const mutation = useMutation ((updatedUser) =>

 

axios.put (`http: //localhost:3004/users/${id}`, updatedUser)

 

) ;

 

Пример удаления мутации:

 

const deleteMutation = useMutation ((id) =>

 

axios.delete (`http: //localhost:3004/users/${id}`)

 

) ;

 

Если ваш код мутации выполняется на странице, где отображаются данные вашего запроса, вы заметите, что после фиксации ничего не меняется. Чтобы вызвать повторную выборку данных после успешной мутации, вам нужно выполнить queryClient.invalidateQueries () функцию. См. пример ниже, где его вызывать:

 

import { useMutation, useQueryClient } from «react-query»;

 

function UserTable () {

 

const deleteMutation = useMutation (

 

(id) => axios.delete (`http: //localhost:3004/users/${id}`),

 

{

 

onSuccess: () => {

 

queryClient.invalidateQueries () ;

 

},

 

}

 

) ;

 

}

 

Ознакомьтесь с полным справочным документом, чтобы useMutationузнать обо всех состояниях и функциях, которые он поддерживает.

 

Резюме

 

В заключение, React Query — отличная библиотека управления состоянием сервера для упрощения ваших потребностей в выборке данных. Есть много других функций, которые мы не рассмотрели, которые могут помочь вам реализовать еще более продвинутые варианты использования. Если вам интересно, есть ли какие-либо другие альтернативы React Query, вам следует ознакомиться с их руководством по сравнению, чтобы получить подробное представление о том, какие функции предлагает каждая библиотека.

3D-печать5GABC-анализAndroidAppleAppStoreAsusCall-центрChatGPTCRMDellDNSDrupalExcelFacebookFMCGGoogleHuaweiInstagramiPhoneLinkedInLinuxMagentoMicrosoftNvidiaOpenCartPlayStationPOS материалPPC-специалистRuTubeSamsungSEO-услугиSMMSnapchatSonyStarlinkTikTokTwitterUbuntuUp-saleViasatVPNWhatsAppWindowsWordPressXiaomiYouTubeZoomАвдеевкаАктивные продажиАкцияАлександровск ЛНРАлмазнаяАлчевскАмвросиевкаАнализ конкурентовАнализ продажАнтимерчандайзингАнтрацитАртемовскАртемовск ЛНРАссортиментная политикаБелгородБелицкоеБелозерскоеБердянскБизнес-идеи (стартапы)БрендБрянкаБукингВахрушевоВендорВидеоВикипедияВирусная рекламаВирусный маркетингВладивостокВнутренние продажиВнутренний маркетингВолгоградВолновахаВоронежГорловкаГорнякГорскоеДебальцевоДебиторкаДебиторская задолженностьДезинтермедитацияДзержинскДивизионная система управленияДизайнДимитровДирект-маркетингДисконтДистрибьюторДистрибьюцияДобропольеДокучаевскДоменДружковкаЕкатеринбургЕнакиевоЖдановкаЗапорожьеЗимогорьеЗолотоеЗоринскЗугрэсИжевскИловайскИрминоКазаньКалининградКировскКировскоеКомсомольскоеКонстантиновкаКонтент-маркетингКонтент-планКопирайтингКраматорскКрасноармейскКрасногоровкаКраснодарКраснодонКраснопартизанскКрасный ЛиманКрасный ЛучКременнаяКураховоКурскЛисичанскЛуганскЛутугиноМакеевкаМариупольМаркетингМаркетинговая информацияМаркетинговые исследованияМаркетинговый каналМаркетинг услугМаркетологМарьинкаМедиаМелекиноМелитопольМенеджментМерчандайзерМерчандайзингМиусинскМолодогвардейскМоскваМоспиноНижний НовгородНиколаевНиколаевкаНишевой маркетингНовоазовскНовогродовкаНоводружескНовосибирскНумерическая дистрибьюцияОдессаОмскОтдел маркетингаПартизанский маркетингПервомайскПеревальскПетровскоеПлата за кликПоисковая оптимизацияПопаснаяПравило ПаретоПривольеПрогнозирование продажПродвижение сайтов в ДонецкеПроизводство видеоПромоПромоушнПрямой маркетингРабота для маркетологаРабота для студентаРазработка приложенийРаспродажаРегиональные продажиРекламаРеклама на асфальтеРемаркетингРетро-бонусРибейтРитейлРовенькиРодинскоеРостов-на-ДонуРубежноеСамараСанкт-ПетербургСаратовСватовоСвердловскСветлодарскСвятогорскСевастопольСеверодонецкСеверскСедовоСейлз промоушнСелидовоСимферопольСинергияСколковоСлавянскСнежноеСоздание сайтов в ДонецкеСоледарСоциальные сетиСочиСтаробельскСтаробешевоСтахановСтимулирование сбытаСуходольскСчастьеТелемаркетингТельмановоТираспольТорговый представительТорезТрейд маркетингТрейд промоушнТюменьУглегорскУгледарУкраинскХабаровскХарцызскХерсонХостингЦелевая аудиторияЦифровой маркетингЧасов ЯрЧелябинскШахтерскЮжно-СахалинскЮнокоммунаровскЯндексЯсиноватая