Skip to main content

Запити в тілі компонента

Чому не можна робити запити в тілі компонента

1. Компонент може рендеритися багато разів

React викликає компонент як звичайну функцію при кожному рендері:

javascript
function User() { console.log('render'); // ... return <div>...</div>; }

Якщо всередині тіла компонента ви напишете запит:

javascript
function User() { fetch('/api/user') // невірно return <div>...</div>; }

цей fetch() буде виконуватися при кожному рендері, а рендерів може бути десятки:

  • через зміну state;
  • через зміну props;
  • через контекст;
  • через батьківський рендер;
  • у React 18 - навіть двічі в dev-режимі (Strict Mode).

У результаті ви отримаєте безліч однакових запитів, навантаження на мережу і "мерехтливий" інтерфейс.


2. React вимагає чистих компонентів

Компонент у React має бути чистою функцією:

та сама функція з однаковими вхідними даними (props/state) завжди повинна повертати однаковий JSX без побічних ефектів.

Асинхронний запит (fetch, axios, localStorage, WebSocket) - це побічний ефект: він змінює зовнішній світ, робить HTTP-виклик і не є детермінованим.

React очікує, що:

javascript
render(props)JSX

а не:

javascript
render(props) → побічний ефект + JSX

Тому такі операції потрібно виносити в ефекти (useEffect), які React виконує після рендера, коли безпечно виконувати асинхронні дії.


3. Асинхронні операції ламають фазу рендера

Якщо зробити await прямо в тілі:

javascript
async function User() { const res = await fetch('/api/user'); // невірно const user = await res.json(); return <p>{user.name}</p>; }

React не буде чекати цей await - він просто побачить, що компонент повернув Promise замість JSX, і видасть помилку.

Виняток - React Server Components (RSC) у Next.js 13+: там серверні компоненти дійсно можуть бути async, тому що рендер виконується на сервері, де React вміє "дочекатися" await.

Але в клієнтських компонентах (звичайні React-застосунки) це неможливо.


4. Ризик нескінченного циклу

Якщо ви записуєте результат запиту в стан прямо в тілі:

javascript
function User() { const [data, setData] = useState(); fetch('/api/user') .then(res => res.json()) .then(setData); return <p>{data?.name}</p>; }

то при кожному setData() -> викликається новий рендер -> знову fetch() -> знову setData()... Нескінченний цикл запитів і завислий інтерфейс.


Як правильно

Використовувати useEffect():

javascript
function User() { const [data, setData] = useState(null); useEffect(() => { fetch('/api/user') .then(res => res.json()) .then(setData) .catch(console.error); }, []); // порожній масив залежностей = виконається один раз при монтуванні if (!data) return <p>Loading...</p>; return <p>{data.name}</p>; }

Підсумок

Робити запит у тілі компонентаРобити запит у useEffect
Виконується при кожному рендеріВиконується після монтування
Створює побічний ефект під час рендераПобічний ефект виконується контрольовано
Може призвести до нескінченних запитівВиконується один раз або за потрібними залежностями
Порушує принцип "чистої функції"Дотримується React-парадигми "рендер -> ефект"

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.