Пошук уроків, статей та іншого контенту
Розглянемо етапи рендерингу React-компонента: від зміни стану або пропсів до формування елементів інтерфейсу.
Рендеринг — це процес, під час якого React визначає, який інтерфейс потрібно показати користувачу.
Компонент React описує інтерфейс через JSX:
function Greeting() {
return <h1>Привіт!</h1>;
}React викликає компонент, отримує JSX і на його основі створює або оновлює елементи інтерфейсу в DOM.
Важливо розрізняти:
виклик компонента — React виконує його функцію;
рендеринг — React обчислює нове представлення інтерфейсу;
оновлення DOM — React застосовує необхідні зміни до сторінки.
Рендеринг компонента не означає, що React повністю перемальовує всю сторінку в браузері.
Рендеринг відбувається в таких основних випадках:
Під час першого запуску застосунку.
Коли змінюється стан компонента.
Коли компонент отримує нові пропси.
Коли повторно рендериться його батьківський компонент.
Під час запуску застосунку React отримує кореневий компонент і створює для нього інтерфейс:
import { createRoot } from "react-dom/client";
import App from "./App.jsx";
const root = createRoot(document.getElementById("root"));
root.render(<App />);Виклик root.render(<App />) запускає перший рендеринг:
React викликає компонент App.
Компонент повертає JSX.
React створює відповідні DOM-елементи.
React додає їх до елемента root.
Якщо компонент змінює свій стан за допомогою функції, яку повертає useState, React запускає новий рендеринг цього компонента.
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Натиснуто: {count}
</button>
);
}Після натискання кнопки відбувається така послідовність:
Виконується обробник onClick.
Викликається setCount.
React зберігає нове значення стану.
React повторно викликає Counter.
Компонент повертає JSX уже з новим значенням count.
React порівнює нове представлення з попереднім.
React оновлює текст кнопки в DOM.
React не викликає setCount для миттєвої зміни змінної count. Функція повідомляє React, що стан потрібно оновити.
Пропси передаються від батьківського компонента до дочірнього. Якщо батьківський компонент передає дочірньому нове значення, дочірній компонент рендериться з новими пропсами.
function UserName({ name }) {
return <p>Користувач: {name}</p>;
}
function App() {
return <UserName name="Олена" />;
}У цьому прикладі name — проп дочірнього компонента UserName.
Якщо батьківський компонент передасть:
<UserName name="Андрій" />React викличе UserName з новим значенням і оновить текст у DOM.
Оновлення інтерфейсу в React можна поділити на два етапи:
Фаза рендерингу — React обчислює, яким має бути інтерфейс.
Фаза фіксації змін — React застосовує потрібні зміни до DOM.
На цій фазі React:
викликає компоненти;
отримує JSX;
створює нове внутрішнє представлення інтерфейсу;
порівнює його з попереднім представленням.
Наприклад, після зміни лічильника React може порівняти такі результати:
<button>Натиснуто: 0</button>і:
<button>Натиснуто: 1</button>React бачить, що змінився лише текст усередині кнопки.
Фаза рендерингу не повинна змінювати DOM або виконувати побічні дії.
Після порівняння React застосовує зміни до реального DOM:
додає нові елементи;
видаляє непотрібні елементи;
змінює текст;
оновлює атрибути;
додає або видаляє обробники подій.
У попередньому прикладі React не створює кнопку заново. Він змінює лише текст, який став іншим.
import { useState } from "react";
function Greeting({ name }) {
return <h1>Привіт, {name}!</h1>;
}
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount((previousCount) => previousCount + 1);
}
return (
<section>
<p>Кількість натискань: {count}</p>
<button onClick={handleClick}>Збільшити</button>
</section>
);
}
function App() {
return (
<main>
<Greeting name="Олена" />
<Counter />
</main>
);
}
export default App;Під час першого рендерингу:
React викликає App.
App повертає Greeting і Counter.
React викликає Greeting з пропом name.
React викликає Counter.
Counter отримує початковий стан count, що дорівнює 0.
React створює заголовок, абзац і кнопку в DOM.
Після натискання кнопки:
Викликається handleClick.
setCount повідомляє React про зміну стану.
React повторно викликає Counter.
Counter отримує нове значення count.
React порівнює попередній і новий результат.
У DOM змінюється лише текст абзацу.
Компонент Greeting у цей момент не має власного стану, який змінюється. Його інтерфейс залишається таким самим.
Під час рендерингу компонент повинен лише обчислювати JSX на основі своїх пропсів і стану.
Добре:
function Price({ amount }) {
const formattedAmount = `${amount} грн`;
return <p>{formattedAmount}</p>;
}Цей компонент отримує дані, обчислює результат і повертає JSX.
Не варто змінювати під час рендерингу:
DOM напряму;
змінні за межами компонента;
стан через setState;
дані, які використовують інші компоненти.
Наприклад, не слід викликати оновлення стану безпосередньо в тілі компонента:
function Counter() {
const [count, setCount] = useState(0);
// Неправильно: це може спричинити нескінченні рендеринги
setCount(count + 1);
return <p>{count}</p>;
}У цьому випадку кожен рендеринг запускає нову зміну стану, а кожна зміна стану запускає новий рендеринг.
Зміни стану потрібно виконувати у відповідь на подію або в іншому спеціальному механізмі React, а не безпосередньо під час обчислення JSX.
Коли батьківський компонент повторно рендериться, React зазвичай повторно викликає і його дочірні компоненти.
function Child() {
console.log("Child render");
return <p>Дочірній компонент</p>;
}
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>
{count}
</button>
<Child />
</div>
);
}Після натискання кнопки Parent рендериться знову. Разом із ним React може повторно викликати Child.
Це не означає, що весь DOM буде повністю створено заново. React порівняє результати рендерингу та застосує лише необхідні зміни.
Повторний виклик компонента не обов’язково змінює сторінку.
function Message() {
console.log("Компонент рендериться");
return <p>Незмінне повідомлення</p>;
}React може повторно викликати Message, але якщо результат залишився таким самим, реальний DOM не потребує змін.
Тому потрібно розділяти:
компонент рендериться — функція компонента була викликана;
DOM оновився — React знайшов відмінності та застосував їх.
У спрощеному вигляді оновлення React-компонента виглядає так:
Виникла причина для оновлення:
змінився стан;
змінилися пропси;
повторно рендериться батьківський компонент.
React планує новий рендеринг.
React викликає потрібні компоненти.
Компоненти повертають новий JSX.
React порівнює новий результат із попереднім.
React застосовує мінімальні зміни до DOM.
Браузер відображає оновлений інтерфейс.
setStatefunction Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
console.log(count);
}
return <button onClick={handleClick}>{count}</button>;
}У цьому прикладі console.log може вивести попереднє значення. Виклик setCount планує оновлення, але не змінює значення count у поточному виконанні функції.
Якщо наступне значення залежить від попереднього, використовуйте функціональний запис:
setCount((previousCount) => previousCount + 1);Для об’єктів і масивів не слід змінювати наявне значення безпосередньо. Потрібно створити нове значення і передати його в оновлення стану.
const [user, setUser] = useState({
name: "Олена",
});
// Правильно: створюється новий об'єкт
setUser((previousUser) => ({
...previousUser,
name: "Андрій",
}));Запити до сервера, роботу з DOM або інші побічні дії не потрібно виконувати просто в тілі компонента. Рендеринг має залишатися передбачуваним: однакові пропси та стан мають давати однаковий результат JSX.
React може повторно викликати компоненти, але під час фази фіксації застосовує лише потрібні зміни до DOM. Повторний рендеринг компонента не означає повне перезавантаження сторінки.
Рендеринг — це обчислення інтерфейсу на основі пропсів і стану.
Перший рендеринг запускається через root.render.
Зміна стану або пропсів може запустити новий рендеринг.
Під час фази рендерингу React викликає компоненти та отримує JSX.
Під час фази фіксації React застосовує необхідні зміни до DOM.
Повторний рендеринг компонента не означає повне перемальовування DOM.
Компоненти мають залишатися чистими під час рендерингу.
Стан потрібно змінювати через функції оновлення, а не напряму.