React Query - залог эффективных запросов



Книга React Query - залог эффективных запросов

В статье будут рассмотрены: 


  • случаи использования React Query; 
  • простые запросы fetch с помощью пакета React Query; 
  • поиск элементов через API по ID; 
  • пагинация; 
  • мутации. 

Случаи использования React Query 


Традиционный метод fetch() отлично подходит для извлечения данных с помощью API. Однако по мере разрастания и усложнения приложения вы можете столкнуться с рядом трудностей. Например: 


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

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


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


Теперь же, обсудив ее преимущества, приступаем к написанию кода! 


React Query: основы


В данном разделе статьи будут рассмотрены:


  • простые вызовы API; 
  • поиск по ID. 

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


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


Ввод команды в терминале для инициализации репозитория 

В данном проекте применяются такие сторонние библиотеки, как: 


  • React-query  —  для выполнения запросов fetch и post к API. 
  • Formik  —  для создания текстовой формы, позволяющей пользователю осуществлять поиск данных по ID. 

Для их установки вводим в терминал команду: 


Ввод команды в терминале для установки пакетов 

После этого переходим в src/App.js и удаляем код между тегами div. В итоге файл src/App.js должен выглядеть таким образом: 


В App.js код должен выглядеть именно так 

Получаем следующий результат: 


Результат кода 

Теперь переходим к выполнению простых запросов fetch с помощью React Query.


Получение и отображение данных 


На первом этапе создаем файл Passenger.js в директории src. Он отвечает за получение данных с сервера. В статье используется Fake REST API


В src/Passenger.js прописываем код: 


import { useQuery } from "react-query";

function Passengers() {
const { isLoading, error, data, isSuccess } = useQuery("passengers", () =>
fetch(
"https://api.instantwebtools.net/v1/passenger?page=0&size=10"
).then((res) => res.json())
);
return (
<div>
{isSuccess &&
data.data.map((item) => (
<div key={item._id}>
<p>{item.name}</p>
<p>{item._id}</p>
</div>
))}
{isLoading && <p>Loading..</p>}
{error && <p>Error occurred!</p>}
</div>
);
}

export default Passengers;

  • Строка 1. Импортируем метод useQuery из пакета React Query, позволяющий выполнять запросы fetch.
  • Строка 4. Извлекаем поля isLoading, error, data и isSuccess из хука useQuery. Первый параметр useQuery —  это ключ, применяемый для идентификации запроса.  
  • Строки 5-7. Сообщаем React о намерении выполнить запрос fetch к API, после чего преобразуем полученные данные в JSON.
  • Строка 11. Если запрос был успешным (isSuccess является true), то отображаем данные. В нашем случае ими будут поля id и name каждого элемента. 
  • Строки 12–13. Если запрос продолжает загружаться или вернул ошибку (isLoading является true или error не является null), то показываем надлежащее сообщение. 

Далее переходим в App.js и импортируем QueryClient, QueryClientProvider из пакета React Query , а также компонент Passengers:


Код для App.js

Непосредственно поверх объявления компонента App пишем следующий фрагмент кода: 


Код для App.js

  • Строка 1. Создаем экземпляр QueryClient для взаимодействия с кэшем. 

Теперь находим данный фрагмент кода в App.js:


Код, который нужно найти в App.js

Заменяем его блоком кода, указанным ниже: 


Код для замены 

  • Строка 3. QueryClientProvider служит мостом между приложением и QueryClient, иначе говоря, позволяет реализовать кэширование в приложении. 
  • Строка 4. Отрисовываем компонент Passengers.

Выполняем код, получая следующий результат: 


Вывод кода 

Как видим, код работает. Простой запрос fetch успешно выполнен с помощью React Query. 


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


В итоге App.js должен выглядеть так: 


import { QueryClient, QueryClientProvider } from "react-query";
import Passengers from "./Passengers";

const queryClient = new QueryClient();
function App() {
return (
<div>
<QueryClientProvider client={queryClient}>
<Passengers />
</QueryClientProvider>
</div>
);
}

export default App;

Поиск по ID 


В каталоге src создаем файл PassengerID.js. Компонент PassengerID позволит пользователю искать данные пассажира всего лишь путем ввода ID.


В src/PassengerID.js начинаем с импорта библиотек: 


Код для PassengerID

  • Строка 1. Используем переменную состояния для отслеживания ID. 
  • Строка 2 помогает в выполнении запросов к API. 
  • Хук useFormik содействует в создании форм. 

Далее пишем код в файле PassengerID:


function PassengerID() {
const [id, setID] = useState("");
const formik = useFormik({
initialValues: {
_id: "",
},
onSubmit: (values) => {
console.log(JSON.stringify(values, null, 2));
setID(values._id);
},
});
const fetchPassenger = async (id) => {
const res = await fetch(
`https://api.instantwebtools.net/v1/passenger/${id}`
);
return res.json();
};
const { data, error, isLoading } = useQuery(["passengerID", id], () =>
fetchPassenger(id)
);
}

export default PassengerID;

  • Строка 2. Хук id сообщает React Query идентификатор, по которому нужно произвести запрос через API.
  • Строка 3. Хук useFormik помогает в создании формы. Здесь мы информируем Formik о том, что начальное значение текстового поля _id будет пустым. 
  • Строка 7. Если пользователь отправляет форму, то вызываем функцию setID для изменения переменной id на значение, введенное им в текстовое поле. 
  • Строка 12. Объявляем функцию fetchPassenger, которая выберет пользователя в соответствии с ID, присутствующим в параметре. В итоге сырые данные будут преобразованы в JSON и затем возвращены. 
  • Строка 18. Запускаем функцию useQuery для выполнения запроса fetch к API. Обратите внимание на добавление в ключ переменной состояния id. Дело в том, что наш запрос зависит от переменной id. Таким образом мы даем указание React выполнять запрос при каждом изменении состояния id
  • Строка 19. Вызываем функцию fetchPassenger и передаем параметр id

Данные извлечены, осталось их только отобразить. 


В довершении всего добавляем код в PassengerID.js:


return (
<div>
<h1>Find by ID</h1>
<form onSubmit={formik.handleSubmit}>
<input
id="_id"
name="_id"
type="text"
onChange={formik.handleChange}></input>
</form>
{error && <p>Error!</p>}
{data && (
<p>
{data.name}, {data.trips}
</p>
)}
{isLoading && <p>Loading..</p>}
</div>
);

  • Строка 4. Создаем элемент form и в случае отправки пользователем формы инструктируем React запустить соответствующий обработчик.
  • Строка 5. Создаем текстовое поле input с id=”_id" и name="_id". Необходимо отметить, что мы передаем это поле id и name, соответствующее свойству, которое было определено ранее в свойстве initialValues, расположенном в хуке useFormik
  • Строка 12. Если данные возвращаются, то отображаем поля name и trips (поездок) пассажира.  

Теперь код принял свой окончательный вид. На этом этапе необходимо отрисовать компонент PassengerID в DOM.


В App.js находим фрагмент кода: 


Код, который нужно найти в App.js

Меняем его следующим образом: 


Код для App.js

  • Строка 5. Отрисовываем компонент PassengerID.

Выполняем код, в результате получая: 


Вывод кода 

Убеждаемся в работоспособности кода. Обратите внимание, что при каждом изменении переменной состояния id, происходит повторное извлечение данных. 


Тема данного раздела исчерпана, и пора переходить к следующему, в котором подробно будем знакомиться с библиотекой React Query.


Напоследок покажем, как должен выглядеть PassengerID.js:


import { useState } from "react";
import { useQuery } from "react-query";
import { useFormik } from "formik";
import Passengers from "./Passengers";

function PassengerID() {
const [id, setID] = useState("");
const formik = useFormik({
initialValues: {
_id: "",
},
onSubmit: (values) => {
console.log(JSON.stringify(values, null, 2));
setID(values._id);
},
});
const fetchPassenger = async (id) => {
const res = await fetch(
`https://api.instantwebtools.net/v1/passenger/${id}`
);
return res.json();
};
const { data, error, isLoading } = useQuery(["passengerID", id], () =>
fetchPassenger(id)
);
return (
<div>
<h1>Find by ID</h1>
<form onSubmit={formik.handleSubmit}>
<input
id="_id"
name="_id"
type="text"
onChange={formik.handleChange}
></input>
</form>
{error && <p>Error!</p>}
{data && (
<p>
{data.name}, {data.trips}
</p>
)}
{isLoading && <p>Loading..</p>}
</div>
);
}

export default PassengerID;

React Query: дополнительные возможности 


В этом разделе рассмотрим: 


  • пагинацию; 
  • мутации. 

Начнем с пагинации. 


Пагинация 


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


В файле Passengers.js объявляем переменную состояния page:


Код для Passengers.js

Это позволит отслеживать текущую страницу. 


Далее пишем такой фрагмент кода: 


Код для Passengers.js

В этой функции мы инструктируем React извлечь записи через API с указанной страницы. В итоге возвращаются преобразованные данные JSON. 


Далее находим следующий фрагмент кода: 


Код, который нужно найти в Passengers.js

Теперь меняем его таким образом: 


Код для Passengers.js

  • Строка 2. Сообщаем React, что запрос зависит от переменной page. При изменении состояния page выполняем запрос заново. 
  • Строка 3. Извлекаем данные пассажира с помощью переменной page в качестве параметра. 
  • Строка 4. keepPreviousData дает указание React сохранять старые данные при изменении ключа запроса. 

Мы почти у цели. Далее находим блок return в Passengers.js:


Код, который нужно найти в Passengers.js

Добавляем следующие строки кода сразу после открывающего тега div:


Код для Passengers.js

  • Строка 3. Инструктируем React уменьшить состояние page и остановить его при достижении 0. 
  • Строка 4. Увеличиваем состояние page.
  • Строка 5. Отображаем значение page.

Вот теперь закончили! Выполняем код, в результате получая: 


Вывод кода 

Как видим, код работает. Теперь займемся мутациями. 


Окончательный вариант кода Passengers.js:


import { useState } from "react";
import { useQuery } from "react-query";

function Passengers() {
const [page, setPage] = useState(0);

const fetchPassengers = async (page) => {
const res = await fetch(
`https://api.instantwebtools.net/v1/passenger?page=${page}&size=10`
);
return res.json();
};

const { isLoading, error, data, isSuccess } = useQuery(
["passengers", page],
() => fetchPassengers(page),
{ keepPreviousData: false }
);
return (
<div>
<button onClick={() => setPage((old) => Math.max(0, old - 1))}>
{" "}
-{" "}
</button>
<button onClick={() => setPage((old) => old + 1)}> + </button>

<p> {page} </p>
{isSuccess &&
data.data.map((item) => (
<div key={item._id}>
<p>{item.name}</p>
<p>{item._id}</p>
</div>
))}
{isLoading && <p>Loading..</p>}
{error && <p>Error occurred!</p>}
</div>
);
}

export default Passengers;

Мутации 


React Query  —  отличная библиотека для выполнения запросов GET. Выясним, как изменять или добавлять данные на сервер. 


Здесь в дело вступают мутации, позволяющие выполнять запросы POST и PUT.


Но прежде установим axios для осуществления вышеуказанных запросов к API. 


Ввод команды в терминал для установки axios

В директории src создаем файл AddPassenger.js. Начинаем с импорта модулей: 


Код для AddPassenger.js

  • Строка 1. Объявляем переменные состояния для последующей их отправки к API в качестве данных. 
  • Строка 2. Хук useMutation инструктирует React изменить данные на сервере. 
  • Строка 3. Axios разрешает выполнить запросы POST к API.
  • Строка 4 способствует извлечению данных из формы. 

function AddPassenger() {
const formik = useFormik({
initialValues: {
name: "",
trips: 0,
airline: 1,
},
onSubmit: (values) => {
console.log(JSON.stringify(values, null, 2));
mutation.mutate({
name: values.name,
trips: values.trips,
airline: values.airline,
});
},
});
}
export default AddPassenger;

  • Строки 2–6. Создаем хук useFormik, имеющий 3 значения name, trips и airline.
  • Строки 8–16. При отправке пользователем формы выполняем мутацию. Отправляем значения текстовых полей в качестве данных на сервер. В дальнейшем определяем mutation.

Затем пишем фрагмент код:


const mutation = useMutation((item) =>
axios.post("https://api.instantwebtools.net/v1/passenger/", item)
);
if (mutation.isSuccess) console.log(mutation.data.data);

  • Строка 1. Создаем экземпляр useMutation.
  • Строка 2. Выполняем запрос POST к API. Переменная item является телом данных, которые будут отправлены позже. 
  • Строка 4. Если мутация была успешной (свойство isSuccess является true), то регистрируем возвращаемые данные в консоли. 

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


Пишем следующий код в файле AddPassenger.js:


return (
<div>
<h1>Submit form</h1>
<form onSubmit={formik.handleSubmit}>
<label>
Name
<input id="name" type="text" onChange={formik.handleChange} />
</label>
<label>
Trips
<input id="trips" type="number" onChange={formik.handleChange} />
</label>
<label>
Airline:
<input id="airline" type="number" onChange={formik.handleChange} />
</label>
<button type="submit">Submit</button>
</form>
{mutation.isLoading && <p>Please wait</p>}
{mutation.isSuccess && <p>Success! ID: {mutation.data.data._id}</p>}
</div>
);

  • Строка 4. Запускаем обработчик отправки Formik, когда пользователь отправляет форму. 
  • Строки 7–15. Создаем несколько текстовых полей для name, trips и airline.
  • Строка 19. Если запрос загружается (свойство isLoading является true), то отображаем надлежащее сообщение. 
  • Строка 20. В случае успешного выполнения мутации (свойство isSuccess является true) отображаем ID нового пассажира. 

Работа над этим кодом завершена, осталось отобразить компонент AddPassenger в DOM.


Переходим к src/App.js и находим блок кода: 


Код, который нужно найти в App.js

Меняем его таким образом: 


Замена кода в App.js

  • Строка 4. Отображаем компонент AddPassenger

Выполняем код и получаем результат: 


Вывод кода 

Код работает. Мы получили ID пассажира. Попробуем его найти. 


Поиск по ID 

Все прекрасно функционирует! 


Дополнительные источники 


Репозиторий GitHub 



Заключение 


Вот такая неимоверно легкая в применении React Query. Нам не пришлось писать много кода для добавления поддержки пагинации или выполнения запроса POST. Неудивительно, что крупные корпорации, например Microsoft и eBay, используют для продакшена именно React Query.


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


Благодарим за внимание! 


654   0  

Comments

    Ничего не найдено.