useEffect useState fetch
Полезные ссылки:
📚 Введение
На предыдущих уроках мы разобрали базовый хук useState и основные принципы работы с ним, но на
этом занятии разберем не мало важный хук как useEffect который используется в 90% задач при
создании веб-приложения с помощью React.
Вот на чем мы остановились:
🪄 1. Монтирование и обновление компонент
При первой загрузке приложения компонент проходит стадию монтирования:
Reactинициализирует состояние (например, черезuseState),- запоминает его начальное значение.
Далее, когда мы изменяем это состояние с помощью функций вроде setSelectedTrackId, запускается
стадия обновления:
Reactсравнивает текущее дерево элементов с предыдущим (процесс diffing),- и в зависимости от различий выборочно перерисовывает компонент или его часть.
Mount (монтирование) — стадия, когда компонент создаётся впервые.
Update (обновление) — стадия, когда React повторно запускает компонент, потому что
изменилось его состояние или пришли новые пропсы.
Рендер в контексте React — это не пиксели, которые видит пользователь, а вызов
компонента и JSX-разметка, которую этот компонент возвращает.
️️🏗️ 2. useState для tracks
Создадим новый useState где определим две переменных tracks и setTracks, также определим
инициализационное состояние этого useState
После того как мы добавили и стали использовать переменную tracks из useState, мы увидим, что
ничего не изменилось. Это происходит потому, что для инициализации использовалось предыдущее
значение переменной, и при монтировании всё осталось прежним.

Но если мы определим null для useState как инициализационное значение то мы увидим другой
результат

И если мы определим пустой массив для useState как инициализационное значение то мы увидим
другой результат

И так что мы можем резюмировать что React:
- вызывает компонент
App - создает для нее
Fiber Nodeрегистрируя туда все состояния к примеру наши useState которые мы определили - получает
JSXи рисует его пользователю в зависимостях которые мы прописали в компоненте
👨🏻🏫 3. effect и useEffect
useEffect — это хук, который позволяет выполнять побочные эффекты (side effects), такие как
запросы на сервер или другие действия, на этапе монтирования компонента или при обновлении его
состояния/пропсов.
И так посмотрим когда нам нужен useEffect и для этого создадим некоторую функцию effect при
вызове которой на этапе монтирования мы сделаем "запрос на сервер".
Что же мы увидим:
- При монтировании компонента вызывается функция effect.
- При изменении состояния через useState — например, при клике на заголовок трека, когда мы изменяем selectedTrackId — происходит обновление компонента и снова вызывается функция effect.
Добавим setTracks в effect и используем те же треки
Что же мы увидим:
- При монтировании компонента функция
effectвызывается многократно, так как происходит повторный ререндер из-за изменения состоянияtracksвнутриeffect. - Появляется ошибка, сигнализирующая о бесконечном цикле вызовов.
Процесс выглядит так:
- Монтируется компонент
App. - Происходит инициализация состояний
tracksиselectedTrackId. - Вызывается функция
effect, которая изменяет состояниеtracksс помощьюsetTracks. - Поскольку состояние обновилось, происходит повторный ререндер
App, и цикл повторяется.
🎓 3.1 Как работает useEffect
Чтобы избежать лишних рендеров компонента, на помощь приходит хук useEffect. Он позволяет
выполнять нужный эффект при монтировании или при изменении определённых условий, которые мы передаем
в массив зависимостей — второй аргумент хука.
- Первый аргумент
useEffect— это функция с эффектом. - Второй аргумент — массив зависимостей.
Если массив зависимостей не передан, эффект выполняется после каждого рендера компонента.
Если массив пустой, эффект выполняется только один раз при монтировании.
🦾 3.2 Используем useEffect
Чтобы установить новое значение tracks только один раз при монтировании и избежать постоянных
ререндеров, мы будем использовать хук useEffect установив пустой массив зависимостей.
📡 4. Выполняем запрос на сервер
Итак, давайте сделаем запрос к серверу. Для этого нам понадобится:
fetch— с помощью этого метода мы выполним запрос.- URL-адрес — это первый аргумент, который мы передаём в вызов
fetch:
- API-ключ — параметр, который мы передаём в заголовок запроса. Этот ключ нужно взять на нашем сайте apihub
Что происходит:
- При обновлении страницы первое, что мы увидим, — это данные, которые были заданы в стейте до выполнения запроса:

- После выполнения запроса всё пропадает. Почему это происходит и что случилось? Нам нужно исследовать эту проблему.
🧐 5. Анализ и решение ошибки
После выполнения запроса мы получаем некоторые данные, которые необходимо отобразить. Важно понять, какова структура этих данных и как правильно использовать полученный ответ.
Как мы можем проанализировать, что именно пошло не так и почему сайт работает не так, как ожидается:
- Откройте вкладку
Sourcesв панели разработчика, попасть в которую можно с помощью сочетания клавишfn + F12или простоF12. - Найдите файл
App.tsx.

- Поставьте точку останова на строке, где используется метод
mapдля предполагаемого массива, затем перезагрузите страницу с помощью комбинации клавишcommand + Rилиctrl + R.

- При наведении курсора вы обнаружите, что это не
массив, аобъект, который содержит совершенно иную структуру данных. Эту структуру необходимо проанализировать и корректно использовать.
Итак, предполагается, что в ответе сервера есть поле data, в котором содержится массив треков для
отображения. Поэтому при сохранении данных в состояние необходимо сетать не просто весь объект
json, а именно json.data.
Посмотрим, что получилось:

Удалось отобразить треки, но не отображаются их названия, и отсутствует возможность воспроизведения треков. Разберёмся, в чём причина:
- Снова открываем панель разработчика, переходим во вкладку
Sourcesи смотрим структуру данных.

Как видно, поле title находится внутри вложенного объекта attributes, то есть получить доступ к
названию трека можно по ключу track.attributes.title.
- Одну проблему мы решили — теперь при обновлении страницы явно видно, что название трека отображается корректно:

Однако треки по-прежнему не воспроизводятся. Проверим путь к url.

Видим, что нужный url находится внутри объекта attributes, далее — по ключу attachments,
который является массивом. Внутри первого элемента этого массива находится свойство url.
Теперь треки можно воспроизводить, и все возникшие проблемы были исправлены:

⚙️ 6. Lifecycle Mount Mode

Разберём жизненный цикл компоненты App при монтировании:
Reactсоздаёт для компоненты объектFiberNode, который будет хранить "состояния" этой компоненты. То естьReactготовит место для хранения всех необходимых данных компоненты.- Затем
Reactвызывает функцию-компоненту, начинаетсяrender phase. - Происходит инициализация начальных состояний, например, которые мы определили с помощью
useState. - Фиксируются действия, которые нужно будет выполнить позже, например, какая функция из
useEffectдолжна быть вызвана. - Компонента возвращает
JSX— объектReactElement, который может содержать другие объекты. Этот объект возвращается вReact. - Затем
Reactпереходит к следующей фазе —commit phase, когда на основе полученной информации создаётсяDOM. - Можно увидеть визуальный результат на странице.
- После визуализации
Reactприступает к выполнению зарегистрированных ранее в очередь функций. В нашем примере был зарегистрированuseEffect, который теперь выполняется. - Внутри
useEffectмы делаем запрос к серверу черезfetchи получаем ответ. - После получения ответа мы обновляем состояние с помощью
setTracks(serverTracks), фиксируя новое значение вFiberNode.
🔃 7. Lifecycle Update Mode

Итак, мы зафиксировали новое значение, и tracks изменились. React видит, что данные, влияющие на
отрисовку, изменились, поэтому нужно создать новый результат на основе новых данных, и React
переходит к этапу обновления:
- React повторно вызывает компоненту
App, чтобы обновить DOM, который видит пользователь. На этот разuseEffectне будет вызван, так как он срабатывает только один раз при монтировании. - После этого React снова переходит в
render phase, где формируется новый обновлённыйReactElement. - Затем начинается
commit phase, и React обновляет DOM на основе новой информации. - Теперь можно увидеть визуальный результат после обновления.
🏠 Домашнее задание
Цель задания:
Практически освоить использование хука useEffect для загрузки данных с сервера и понять жизненный
цикл React компонента.
Задание 1
Получение данные с сервера
Продолжай работать в проекте с предыдущего домашнего задания.
- В
App.tsxсоздай состояниеtasksс помощьюuseState, установив изначальное значение вnull - Удали хардкодный массив
tasksиз глобальной области видимости - Напиши
useEffectс пустым массивом зависимостей[] - В эффекте выполни
fetch-запроскAPIза получением тасок - Используй API endpoint: https://trelly.it-incubator.app/api/1.0/boards/tasks
Структура ответа с сервера должна выглядеть вот так 🚀

Количество тасок, их заголовки и другие свойства могут отличаться, т.к. это список всех публичных тасок
Задание 2
Отрисовка данных с сервера
После того как запрос отрабатывает верно перейдем к отрисовке данных
- Преобразуй ответ в
JSON - Установи полученные данные в состояние с помощью
setTasks - Структура данных пришедшая с сервера будет немного отличаться от данных, которые мы хранили в
переменной
tasks. Поэтому внимательно изучи ответ с сервера и отрисуй данные в соответствии со структурой которая пришла с сервера
❗ Статус
Для отображения статуса таски мы использовали свойство isDone. Но сервер для работы со статусом
возвращает свойство status, которое может быть в 4-х состояниях
Соответственно при отрисовке данных замени свойство isDone на status. status === 0 обозначает
невыполненную таску, а status === 2 - выполненную.
Итоговый результат 🚀

Задание 3
Преобразуй дату в понятный для человека формат.
Для реализации данной задача необходимо воспользоваться объектом
Date и
вызвать у него метод .toLocaleDateString()
.toLocaleDateString() — преобразует дату в строку, отформатированную в соответствии с локальными
настройками пользователя (например: 17.04.2025 для русской локали или 4/17/2025 для США)

Доп. задания
Отладка (Debug)
- Открой
DevTools (F12) - Во вкладке
Networkпроверь, что запрос отправляется только один раз при загрузке компонента - При клике на задачи убедись, что дополнительные запросы не отправляются
Ожидаемый результат
- При загрузке страницы должен показываться индикатор "Загрузка..."
- После получения данных с сервера должен отобразиться список задач
- Функциональность выделения задач должна работать как прежде
- Запрос должен выполняться только один раз при монтировании компонента
Вопросы для самопроверки
- Почему мы используем пустой массив зависимостей
[]вuseEffect? - Что произойдет, если не передать массив зависимостей вообще?
- В какой момент жизненного цикла компонента выполняется эффект с пустым массивом зависимостей?
- Почему нельзя вызывать хуки внутри условий или циклов?
Удачи в изучении React! Помни: понимание useEffect — это ключ к освоению современной
разработки на React.


