-
Notifications
You must be signed in to change notification settings - Fork 0
Фетчинг данных
Дисклеймер!
Комментарии в коде на русском обусловлены тем, что это учебно-показательный материал, и в боевом коде они строго под запретом.
Перед прочтением рекомендуется ознакомиться:
- Graphql + Apollo
- React
Graphql — это язык запросов, использующийся для общения между клиентом и сервером в более удобном формате
Apollo — библиотека, упрощающая работу с graphql (в том числе с React)
У нас есть фрагмент – список тудушек. Тудушки необходимо получать из внешнего источника данных, соответственно:
- Сделать запрос на эндпоинт для получения списка задач
- Отображать Loading пока не придут данные
- Отрисовать список задач как только они придут
URL источника данных указывается в ApolloProvider
. При использовании Nextjs это достаточно сделать один раз в _app.tsx
, с этим поможет библиотека next-app-with-apollo
Здесь уже понадобится библиотека @apollo/client
, упрощающая работу с Apollo в React приложении, она предоставляет привычный для react разработки набор инструментов.
Сделаем рабочий прототип (с комментариями):
import React from 'react'
import { List } from '@ui/list'
import { Text } from '@ui/text'
import { FC } from 'react'
import { gql } from '@apollo/client'
import { useQuery } from '@apollo/client'
const TodoList: FC = () => {
// пишем graphql запрос
const GET_TASKS = gql`
query GetTasks {
tasks {
title
done
}
}
`
const { data, loading, error } = useQuery(GET_TASKS) // исполняем запрос
return (
<>
{/* Если идет загрузка - рисуем Loading... */}
{loading && <Text>Loading...</Text>}
{/* Если есть данные - рисуем данные */}
{data && <List items={tasks} />}
</>
)
}
Чтобы компонент узнал о том, что пришли данные и произошел ререндер.
Сам процесс запроса данных разделяется на следующие шаги:
- Создание graphql запроса
- Исполнение запроса
- Обработка ответа
Все эти этапы подтягивают за собой определенное количество шаблонного кода, который мы можем скрыть за более понятными человеку абстракциями.
Сделаем это поэтапно.
Так как работа с данными — это в своем роде инкапсулированый процесс, то создадим директорию data: project/fragments/todo-list/data
. В ней будем описывать всю логику работы с данными.
Запрос вынесем в отдельный файл: project/fragments/todo-list/data/tasks.query.ts
import { gql } from '@apollo/client'
const GET_TASKS = gql`
query GetTasks {
tasks {
title
done
}
}
`
export { GET_TASKS }
Тут ситуация чуть более неочевидная: для исполнения запроса используется хук (useQuery), а хуки, как мы знаем, можно использовать только внутри компонентов.
Это обходится с помощью коллбэка. Создадим файл project/fragments/todo-list/data/useData.ts
:
import { useQuery } from '@apollo/client'
import { GET_TASKS } from './tasks.query'
const useData = () => {
const { data, loading, error } = useQuery(GET_TASKS)
if (error) {
throw new Error(error.message)
}
if (data) {
return [data.tasks, loading]
}
return [[], loading]
}
Здесь уже появились новые элементы:
- Блок с обработкой ошибки - обработка ошибок это правило хорошего тона при разработке, в случае чего очень поможет при отладке.
-
Два ретёрна? — первый отработает только в том случае, если данные пришли. Причем, что стоит заметить, мы возвращаем
data.tasks
, вместо data, чтобы в самом компоненте нам не пришлось приводить объект из ответа к нужному виду. Второй сработает если данных нет. -
Почему во втором ретёрне возвращаем пустой массив? — для безопасности типов. Мы ожидаем получить массив с задачами, но если задач нет — получим пустой массив.
Это необходимо, чтобы исключить вероятность того, что мы будем пытаться вызвать метод
map
у null или undefined.
Теперь наш компонент преобразовался в:
import React from 'react'
import { List } from '@ui/list'
import { Text } from '@ui/text'
import { FC } from 'react'
import { useData } from './data'
const TodoList: FC = () => {
const [tasks, loading] = useData()
return (
<>
{loading && <Text>Loading...</Text>}
{data && <List items={tasks} />}
</>
)
}