Розбираємо реліз React 19: паралельний рендеринг, серверні компоненти та нові хуки
Привіт! Я Микола, розробник з більш ніж п’ятирічним досвідом у продуктовому ІТ. Вже рік працюю як Front-End Developer на технічному SEO-напрямку в Universe Group — українській ІТ-компанії, що запускає глобальні продукти. В цій статті я розповім про новинки в 19 версії React.
Якщо ви думали, що React не може стати ще кращим, то React 19 готовий кинути вам виклик! Адже він містить важливі оновлення, які зроблять процес розробки ще досконалішим та комфортнішим. Серед ключових нововведень:
— Паралельний Рендеринг для покращення користувацького досвіду.
— Серверні Компоненти для оптимізації серверних і клієнтських ресурсів.
— Нові Хуки, що спрощують і вдосконалюють процес розробки.
— Transitions API для просунутішого управління станами.
Заявлені зміни мають на меті не лише спростити написання коду, а й зробити його більш зрозумілим і ефективним. Давайте детальніше розглянемо, що чекає на нас в цьому релізі.
Паралельний Рендеринг
У порівнянні з попередньою версією React 18, яка використовувала синхронний рендеринг, паралельний рендеринг у React 19 пропонує концепцію, що дозволяє розбивати оновлення інтерфейсу на менші асинхронні завдання. Це нововведення значно покращує інтерактивність користувача і забезпечує більш плавний користувацький досвід завдяки можливості React визначити пріоритет та планувати оновлення, а також уникати блокування інтерфейсу.
Уявіть ситуацію, коли ви відкриваєте кілька вкладок у браузері: кожна вкладка завантажується незалежно від інших, і жодна з них не блокує процес завантаження чи взаємодії з іншими вкладками. Подібним чином паралельний рендеринг у React 19 дозволяє різним частинам інтерфейсу завантажуватися та оновлюватися незалежно одна від одної, що забезпечує швидший і комфортніший користувацький досвід.
Ключові переваги
Покращена інтерактивність. Завдяки пріоритетній обробці завдань, інтерфейс швидше реагує на дії користувача, навіть при виконанні складних і обʼємних обчислень. Зменшення часу очікування на оновлення забезпечує швидшу відповідь застосунку на дії користувача, що суттєво покращує його загальне враження від використання застосунку.
Плавніші анімації. Анімації та переходи стають більш плавними та безперервними. React може рендерити кілька анімацій одночасно, що усуває затримки та пригальмовування. Це забезпечує зручніший для користувача досвід.
Оптимізація ресурсів. Паралельний рендеринг дозволяє ефективніше використовувати системні ресурси, що особливо важливо для комплексних застосунків з великою кількістю стейтів. Завдяки цьому підходу проблеми із «зависанням» інтерфейсу під час виконання важких обчислень або запитів до сервера можуть бути значно мінімізовані.
Серверні Компоненти
React Server Components (RSCs) — революційний функціонал, представлений у 19 версії React. Серверні компоненти мають потенціал змінити звичний підхід до створення вебзастосунків із використанням React. Зазвичай React-застосунки покладалися на рендеринг компонентів на стороні клієнта, але нова версія пропонує React Server Components, які можуть безпосередньо рендерити інтерфейс користувача на сервері.
Концепція рендерингу компонентів на стороні сервера попередньо вже була реалізована в таких фреймворках, як Next.js та Astro. Тепер завдяки React Server Components можна значно покращити час завантаження сторінок. Це також покращує SEO, оскільки пошукові системи можуть легше індексувати та розуміти зміст сторінок, адже браузер отримає вже готовий контент.
Нові хуки
useFormStatus
Цей хук дає нам інформацію про статус останньої відправленої форми. При надсиланні форми користувачем він дає змогу отримати такі дані: статус надсилання (успішний, неуспішний, в процесі), надіслані дані, можливість керувати інтерфейсом користувача залежно від результату дії.
const { pending, data, method, action } = useFormStatus();
useFormStatus повертає кілька значень, які дозволяють відстежувати статус форми:
- pending — вказує, чи перебуває форма в процесі надсилання. Якщо форма в цей момент надсилається, значення буде true, а після завершення надсилання — false.
- data — дані, що були надіслані у формі. Це об’єкт, який містить значення всіх полів форми, надісланих під час останнього надсилання.
- method — вказує HTTP-метод, який використовувався для надсилання форми, наприклад, «POST» або «GET».
- action — URL-адреса, на яку було надіслано форму, що дозволяє відстежувати, куди саме було здійснено запит.
import { useFormStatus } from 'react'; function Form() { const { pending, data, method, action } = useFormStatus(); return ( <div> <form action="/submit" method="POST"> <input type="text" name="username" placeholder="Enter username" required /> <button type="submit" disabled={pending}> {pending ? 'Submitting...' : 'Submit'} </button> </form> {pending && <p>Форма все ще відправляється...</p>} {!pending && data && ( <div> <h3>Деталі Відправки:</h3> <p><strong>Метод:</strong> {method}</p> <p><strong>Екшин:</strong> {action}</p> <p><strong>Відправлені дані:</strong> {JSON.stringify(data)}</p> </div> )}
Якщо ви працюєте з формами та хочете покращити їхній менеджмент, а також зберігати чистий і зрозумілий код, цей хук — те, що вам потрібно.
useOptimistic
useOptimistic — хук, який допомагає керувати станом компонентів, припускаючи, що операції будуть успішними. Він дає змогу миттєво оновити інтерфейс, не чекаючи відповіді від сервера. Якщо сервер поверне нам помилку — ми повернемось до початкового інтерфейсу. Функція-хук приймає 2 параметри: 1 — початковий стан компонента (initial state) 2 — функцію, яка приймає поточний стан та оптимістичне значення і повертає оновлений стан.
const [optimisticState, addOptimistic] = useOptimistic( state, // updateFn (currentState, optimisticValue) => { // змержіть стани та поверність оновлений стан із оптимістік значенням } );
У даному прикладі ми використовуємо хук useOptimistic для динамічного оновлення інтерфейсу під час асинхронної операції збереження завдання. Коли користувач додає нову задачу через форму, вона одразу ж з’являється в списку з позначкою «Saving...», поки запит на сервер не завершиться.
import { useOptimistic, useState, useRef } from "react"; async function saveTask(task) { await new Promise((res) => setTimeout(res, 1000)); return task; } function TaskList({ tasks, addTask }) { const formRef = useRef(); async function handleSubmit(formData) { addOptimisticTask(formData.get("task")); formRef.current.reset(); await addTask(formData); } const [optimisticTasks, addOptimisticTask] = useOptimistic( tasks, (state, newTask) => [ ...state, { description: newTask, pending: true } ] ); return ( <> {optimisticTasks.map((task, index) => ( <div key={index}> {task.description} {task.pending && <small> (Збереження...)</small>} </div> ))} <form action={handleSubmit} ref={formRef}> <input type="text" name="task" placeholder="Додайте нову задачу..." /> <button type="submit">Додати</button> </form> </> ); } export default function TaskManager() { const [tasks, setTasks] = useState([ { description: "Завершити проект", pending: false, id: 1 } ]); async function addTask(formData) { const savedTask = await saveTask(formData.get("task")); setTasks((tasks) => [...tasks, { description: savedTask, pending: false }]); } return ; }
Ключова функція оновлення із useOptimistic полягає в тому, що нове завдання відображається одразу як додане для користувача, незалежно від того, чи було його успішно збережено на сервері. Це значно покращує взаємодію користувача з інтерфейсом. Він може миттєво бачити результат дії, не чекаючи завершення операції. Після того, як завдання успішно збережено, позначка «Saving...» зникає.
Use
Новий універсальний хук для зручнішого управління асинхронними даними в React. Він приймає лише один параметр — джерело даних, з яких ми маємо на меті зчитати дані. Наприклад, це може бути контекст чи promise.
const value = use(resource);
Якщо він приймає контекст, то працює ідентично як хук useContext, проте use буде більш гнучким. Адже на відміну від useContext, його можна викликати не лише на верхньому рівні, але й всередині блоків з умовами, циклів. Нижче наведу простий приклад використання хука use для отримання значення контексту:
import React, { createContext, useState, useEffect, use } from 'react'; // Створіть контекст із значенням за замовчуванням const ThemeContext = createContext('light'); // Компонент-Провайдер const ThemeProvider = ({ children }) => { const [currentTheme, setCurrentTheme] = useState('light'); useEffect(() => { // Симулюйте виклик даних const fetchTheme = async () => { await new Promise(resolve => setTimeout(resolve, 500)); setCurrentTheme('dark'); }; fetchTheme(); }, []); return ( <ThemeContext.Provider value={currentTheme}> {children} </ThemeContext.Provider> ); }; const ThemeDisplay = () => { const theme = use(ThemeContext); return <div>Current Theme: {theme}</div>; }; const App = () => ( <ThemeProvider> <ThemeDisplay /> </ThemeProvider> ); export default App;
У випадку, коли значенням є promise, ми можемо використати хук use у парі з компонентом Suspense, щоб зчитати значення promise:
import React, { Suspense, useState, useEffect, use } from 'react'; // Імітуємо отримання даних з сервера const fetchMessage = async () => { // Імітуємо затримку і отримуємо дані з API await new Promise((resolve) => setTimeout(resolve, 2000)); return "Hello from the server!"; }; // Компонент клієнта, який використовує отримані дані const Message = ({ messagePromise }) => { // Використовуємо хук use для отримання вмісту повідомлення з Promise const messageContent = use(messagePromise); return <p>Here is the message: {messageContent}</p>; }; // Основний компонент додатку const App = () => { // Отримуємо повідомлення з сервера; повертає Promise const [messagePromise, setMessagePromise] = useState(null); useEffect(() => { // Ініціалізуємо Promise для повідомлення const initMessage = async () => { const promise = fetchMessage(); setMessagePromise(promise); }; initMessage(); }, []); if (!messagePromise) { return <p>Loading...</p>; // Фолбек варіант до готовності Promise } return ( <Suspense fallback={<p>Waiting for message...</p>}> <Message messagePromise={messagePromise} /> </Suspense> ); }; export default App;
В такому випадку ми передаємо promise в компонент Message за допомогою props, а далі — напряму в хук use. Оскільки компонент Message обгорнутий у Suspense, спочатку буде відображатися fallback, аж доки promise не буде вирішено. Коли promise буде виконано, значення буде прочитане за допомогою API use, і компонент Message замінить fallback у Suspense.
Transition API
Transition API в React дозволяє керувати оновленнями станів, які не є терміновими, надаючи розробникам можливість покращити досвід користувача. Основна ідея полягає в тому, щоб обгортати зміни стану в функцію startTransition. Це сигналізує React, що дані зміни можуть бути відкладені, і можна надати пріоритет більш важливим операціям, як-от анімації чи інтерактивним частинам UI.
import { useState, startTransition } from 'react'; function MyProfileComponent() { const [isPending, setIsPending] = useState(false); const [profileData, setProfileData] = useState(null); const loadProfile = () => { setIsPending(true); startTransition(() => { setTimeout(() => { setProfileData({ name: 'Микола Третьохвильовий', age: 25, occupation: 'Розробник' }); setIsPending(false); }, 2000); }); }; return ( <div> <button onClick={loadProfile}>Load Profile</button> {isPending ? ( <p>Завантаження профілю...</p> ) : ( profileData && ( <div> <p>Ім'я: {profileData.name}</p> <p>Вік: {profileData.age}</p> <p>Професія: {profileData.occupation}</p> </div> ) )} </div> ); } export default MyProfileComponent;
В цьому випадку ми повісили на кнопку подію, яка відповідає за завантаження даних користувача. Обгортаємо функцію-оновлення стану setProfileData в startTransition для того, щоб Transition API мав доступ до нашої дії та оновлюємо наш стан із затримкою у 2 секунди. Основні переваги Transition API:
- Маркування нетермінових оновлень. Дозволяє позначати зміни, які не потребують негайного відображення. Наприклад, великі перетворення даних можуть бути відкладені, щоб не «заморожувати» чи сповільнювати роботу інтерфейсу.
- Плавніші переходи. React може забезпечити комфортну і безперервну взаємодію для користувача, оскільки критичні оновлення обробляються з вищим пріоритетом, ніж другорядні зміни.
- Простота використання. Даний підхід додає мінімум додаткового коду, використовуючи всього одну функцію — startTransition, що робить API зручним і простим для інтеграції.
Використання Transition API дозволить розробникам покращити продуктивність та плавність взаємодії із застосунком, відокремлюючи термінові та нетермінові оновлення стану. Це своєю чергою забезпечить більш комфортний досвід для користувачів, зменшуючи затримки та підвищуючи ефективність роботи з асинхронними операціями, особливо в складних або ресурсомістких застосунках.
SEO
З виходом React 19 управління SEO-елементами стало значно зручнішим і зрозумілішим. Тепер розробники можуть безпосередньо додавати теги заголовків та метатеги у свої компоненти React:
const HomePage = () => { return ( <> <title>Що нового в реакт 19</title> <meta name="description" content="Останні новини та оновлення в React 19" /> {/* Контент сторінки */} </> ); };
Завдяки оновленню, розробники зможуть легше покращувати доступність своїх застосунків — без використання сторонніх бібліотек чи кастомної логіки.
Висновок
Застосунки, які перейдуть на
Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті
28 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів