Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке "optimistic update"?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Optimistic update (оптимістичне оновлення)** - це підхід, за якого **UI оновлюється відразу**, ще *до того*, як сервер підтвердив зміну даних. **Ключове:** застосунок оптимістично припускає, що операція на сервері завершиться успішно, і оновлює інтерфейс миттєво, щоб користувач не відчував затримку.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Що таке "optimistic update" **Optimistic update (оптимістичне оновлення)** - це підхід, за якого **UI оновлюється** ***відразу***, ще *до того*, як сервер підтвердив зміну даних. > Тобто застосунок **оптимістично припускає**, що операція на сервері завершиться успішно, > і оновлює інтерфейс **миттєво**, щоб користувач не відчував затримку. --- ### Приклад без оптимістичного оновлення: ```javascript async function handleLike(postId) { await fetch(`/api/posts/${postId}/like`, { method: "POST" }); refetch(); // чекаємо відповідь від сервера, потім оновлюємо UI } ``` Мінуси: - Кнопку «лайк» натиснуто → але UI не змінився, поки не прийшла відповідь. - Користувач відчуває **затримку 300-500 мс** (або більше при поганій мережі). - Відчувається "загальмований" застосунок. --- ### Приклад з **optimistic update**: ```javascript const queryClient = useQueryClient(); async function handleLike(postId) { // 1. Одразу оновлюємо UI локально queryClient.setQueryData(['post', postId], (old) => ({ ...old, likes: old.likes + 1, isLiked: true, })); try { // 2. Надсилаємо запит на сервер await fetch(`/api/posts/${postId}/like`, { method: 'POST' }); } catch (err) { // 3. Якщо сервер відповів помилкою - відкочуємо зміни queryClient.invalidateQueries(['post', postId]); } } ``` Користувач бачить оновлення **миттєво** - навіть без відповіді від сервера. Коли сервер підтвердить - усе вже синхронізовано. Якщо сервер відповість помилкою - дані **відкочуються** до актуального стану. --- ## Візуальний сценарій | Подія | Що бачить користувач | Що відбувається в коді | | --- | --- | --- | | Натиснув кнопку "лайк" | лайк став активним | `setQueryData()` оновлює кеш негайно | | Сервер отримав запит | нічого не змінюється в UI | `fetch()` виконується у фоні | | Сервер відповів 200 OK | усе залишається як є | кеш лишається актуальним | | Сервер відповів 500 | лайк зникає | відкат (`invalidateQueries` або rollback) | --- ## Чому це важливо ### 1. **Миттєвий відгук** Користувач відчуває, що застосунок працює "у реальному часі" - без затримок, навіть якщо мережа повільна. ### 2. **Плавний UX** Інтерфейс реагує одразу, без "мерехтіння" лоадерів. Оптимістичні оновлення особливо важливі в інтерактивних діях: - лайки, - коментування, - голосування, - зміни імені, статусу, позиції елемента в списку. ### 3. **Незалежність від мережі** Якщо запит не пройшов - можна відкотити локальні зміни. Користувач не втрачає контекст (він бачить, що дія "скасувалася"). --- ## Як реалізується на практиці ### 1. **React Query** ```javascript import { useMutation, useQueryClient } from '@tanstack/react-query'; function useLikePost() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (postId) => fetch(`/api/posts/${postId}/like`, { method: 'POST' }), // 1. Оптимістично оновлюємо кеш onMutate: async (postId) => { await queryClient.cancelQueries(['post', postId]); const prevPost = queryClient.getQueryData(['post', postId]); queryClient.setQueryData(['post', postId], (old) => ({ ...old, likes: old.likes + 1, isLiked: true, })); // 2. Повертаємо дані для rollback return { prevPost }; }, // 3. Якщо помилка - відкочуємо onError: (err, postId, context) => { queryClient.setQueryData(['post', postId], context.prevPost); }, // 4. Після успіху - оновлюємо або інвалідуємо onSettled: (postId) => { queryClient.invalidateQueries(['post', postId]); }, }); } ``` `onMutate` → локальне оновлення `onError` → відкат `onSettled` → фінальне оновлення/валідація --- ### 2. **SWR (Vercel)** ```javascript import useSWR, { mutate } from 'swr'; function likePost(postId) { // оптимістично збільшуємо кількість лайків mutate(`/api/posts/${postId}`, async (data) => { await fetch(`/api/posts/${postId}/like`, { method: 'POST' }); return { ...data, likes: data.likes + 1, isLiked: true }; }, { optimisticData: { ...data, likes: data.likes + 1, isLiked: true }, rollbackOnError: true }); } ``` --- ### 3. **Redux Toolkit Query** RTK Query також дозволяє тимчасово оновлювати кеш локально: ```javascript const [updatePost] = useUpdatePostMutation(); updatePost(newData) .unwrap() .then(() => dispatch(api.util.invalidateTags(['Post']))) .catch(() => revertLocalState()); ``` --- ## 4. Типові кейси для optimistic update | Дія | Що оновлюємо локально | | --- | --- | | Лайк | Збільшуємо лічильник лайків | | Додавання коментаря | Додаємо коментар у список | | Видалення елемента | Видаляємо з UI до відповіді сервера | | Зміна профілю | Змінюємо ім'я/аватар локально | | Зміна порядку | Переставляємо елементи локально | | Оплата / замовлення | Позначаємо "створено" до підтвердження сервера | --- ## Підсумок | Що робить Optimistic Update | Чому важливо | | --- | --- | | Оновлює UI **до** відповіді сервера | миттєвий відгук | | Покращує UX і швидкість сприйняття | немає "затримки" при взаємодії | | Синхронізується при успіху | UI і сервер лишаються узгодженими | | Робить відкат при помилці | надійно при нестабільній мережі | | Скорочує кількість станів `loading` | інтерфейс відчувається "живим" | --- ### Коротко: > **Optimistic update** - це спосіб зробити інтерфейс миттєво відгукливим: > UI оновлюється відразу, *оптимістично припускаючи успіх*, > а якщо сервер повернув помилку - React відкочує зміни.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.