Загрузка данных в useEffect
Загружаем данные с сервера правильным способом и обходим главную ловушку — бесконечный цикл запросов.
Загрузка данных — это побочный эффект, поэтому её место в
useEffect, а результат и статус загрузки хранят в состоянии.
Три состояния загрузки
Любой запрос к серверу проходит фазы: «грузим», «получили данные», «ошибка». Под каждую заводят своё состояние — так UI может показать спиннер, данные или сообщение об ошибке:
function Users() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch("https://api.example.com/users")
.then((res) => res.json())
.then((data) => setUsers(data))
.catch((err) => setError(err.message))
.finally(() => setLoading(false));
}, []); // [] — загрузить один раз при появлении
if (loading) return <p>Загрузка...</p>;
if (error) return <p>Ошибка: {error}</p>;
return (
<ul>
{users.map((u) => <li key={u.id}>{u.name}</li>)}
</ul>
);
}
Почему пустой массив здесь критичен
Обратите внимание на []. Загрузка должна произойти один раз, при появлении компонента. Без массива зависимостей возникает катастрофа — бесконечный цикл.
Главная ошибка: бесконечный цикл
Разберём, как новичок создаёт бесконечный цикл. Если забыть массив зависимостей, цепочка замкнётся:
// ❌ БЕСКОНЕЧНЫЙ ЦИКЛ
useEffect(() => {
fetch(url)
.then((r) => r.json())
.then((data) => setUsers(data)); // меняет состояние...
}); // ...нет массива → эффект после каждой отрисовки
Логика катастрофы по шагам:
- Эффект выполняется →
setUsersменяет состояние. - Смена состояния вызывает перерисовку.
- Без массива зависимостей эффект запускается после каждой перерисовки → снова
fetch→ сноваsetUsers→ шаг 2.
Получается лавина запросов. Лечится одной строкой — пустым массивом [], который говорит «загрузи только один раз».
Загрузка при смене параметра
Если данные зависят от параметра (например, userId), положите его в зависимости — тогда новые данные подгрузятся при каждой смене:
useEffect(() => {
fetch(`/api/users/${userId}`)
.then((r) => r.json())
.then(setUser);
}, [userId]); // перезагрузка при смене userId
Async внутри эффекта
Саму функцию-эффект нельзя делать async (она не должна возвращать промис — это место для функции очистки). Обходят это вложенной async-функцией:
useEffect(() => {
async function load() {
const res = await fetch(url);
const data = await res.json();
setUsers(data);
}
load();
}, []);
Итог
- Загрузка данных — это эффект; держите три состояния:
data,loading,error. - Пустой массив
[]загружает один раз; зависимость[userId]— перезагружает при смене параметра. - Без массива зависимостей
setStateвнутри эффекта создаёт бесконечный цикл запросов.