React’da optimistik interfeys yangilanishlari: patternlar va qaytarish
Chuqur4 daqInnovialabs tahririyati
Qisqacha
Optimistik UI kutilgan natijani darhol ko‘rsatadi va server bilan fonda sinxronlanadi; unga qaytarish uchun snapshot, poyga va dublikatlardan himoya kerak, soxta «muvaffaqiyat» pulga tushadigan joyda esa ishlatilmaydi.
Optimistik UI nima va qachon o‘zini oqlaydi
Optimistik yangilanish — interfeysni so‘rov allaqachon muvaffaqiyatli o‘tgandek o‘zgartirish, server javob bergach esa uni tasdiqlash yoki qaytarish. Layk bir zumda qizaradi, vazifa ro‘yxatda spinnersiz paydo bo‘ladi, nomi o‘zgartirilgan fayl yangi nomini darhol ko‘rsatadi.
Yondashuv uchta shart bajarilganda yaxshi ishlaydi:
- operatsiya deyarli har doim muvaffaqiyatli (validatsiya klientda o‘tgan);
- natijani kirish ma’lumotlaridan oson taxmin qilish mumkin;
- xatoni vizual arzon qaytarish mumkin va u foydalanuvchini muhim narsa haqida chalg‘itmaydi.
Agar bittasi bajarilmasa — halol kutish holatini ko‘rsating.
useOptimistic: React’ning o‘rnatilgan yondashuvi
React 19 da useOptimistic hooki bor. U faqat action yoki transition bajarilayotgan paytda mavjud bo‘ladigan vaqtinchalik holatni qaytaradi; tugagach React siz uzatgan haqiqiy holatga qaytadi.
import { useOptimistic } from "react";
function TodoList({ todos, addTodo }) {
const [optimisticTodos, addOptimistic] = useOptimistic(
todos,
(current, draft) => [...current, { ...draft, pending: true }]
);
async function formAction(formData) {
const draft = { id: crypto.randomUUID(), text: formData.get("text") };
addOptimistic(draft);
try {
await addTodo(draft); // muvaffaqiyatda ota komponentdagi `todos`ni yangilashi shart
} catch {
showToast("Vazifani saqlab bo‘lmadi");
}
}
return (
<form action={formAction}>
<input name="text" required />
<ul>
{optimisticTodos.map((t) => (
<li key={t.id} style={{ opacity: t.pending ? 0.5 : 1 }}>{t.text}</li>
))}
</ul>
</form>
);
}
Asosiysi:
- Qaytarish avtomatik: action xato bersa, transition tugashi bilan optimistik element yo‘qoladi — axir haqiqiy
todoso‘zgarmagan. - Teskari tomoni: muvaffaqiyatda haqiqiy holat ham yangilanishi kerak (revalidatsiya, ota komponentda
setState,revalidatePathbilan Server Action). Aks holda element action tugagach yo‘qoladi. addOptimisticoddiy hodisa ishlovchisida emas, action yokistartTransitionichida chaqiriladi.
TanStack Query: snapshot, yangilash, qaytarish
Ma’lumotlar TanStack Query keshida saqlansa, klassik pattern onMutate ustiga quriladi:
const queryClient = useQueryClient();
const addTodo = useMutation({
mutationFn: (todo: Todo) => api.addTodo(todo),
onMutate: async (todo) => {
await queryClient.cancelQueries({ queryKey: ["todos"] });
const previous = queryClient.getQueryData<Todo[]>(["todos"]);
queryClient.setQueryData<Todo[]>(["todos"], (old = []) => [...old, todo]);
return { previous };
},
onError: (_err, _todo, ctx) => {
queryClient.setQueryData(["todos"], ctx?.previous);
},
onSettled: () => queryClient.invalidateQueries({ queryKey: ["todos"] }),
});
Qadamlar:
- Eski refetch optimistik ma’lumotlarni ustidan yozmasligi uchun joriy so‘rovlarni bekor qiling.
- Keshning snapshotini saqlang.
- Keshga kutilgan natijani yozing.
- Xatoda snapshotni qaytaring va foydalanuvchiga xabar bering.
- Har qanday holatda haqiqat manbasini olish uchun ma’lumotlarni qayta so‘rang.
Soddaroq variant: keshga tegmasdan, isPending true bo‘lgan paytda mutation.variablesni yarim shaffof element sifatida chizing. Qaytaradigan narsa yo‘q — xatoda element shunchaki yo‘qoladi.
Poyga holatlari va dublikatlar
Optimistik UI parallellik muammolarini ko‘zga ko‘proq tashlaydi. Tipik holatlar:
| Muammo | Nima bo‘ladi | Qanday hal qilinadi |
|---|---|---|
| Eskirgan refetch | Mutatsiyadan oldin boshlangan so‘rov eski ma’lumot qaytaradi | Yangilashdan oldin cancelQueries |
| Parallel mutatsiyalar | Birining qaytarilishi boshqasini o‘chiradigan snapshotni tiklaydi | Butun snapshotni emas, faqat o‘z o‘zgarishingizni bekor qiling (id bo‘yicha filtr) |
| Tartibsiz javoblar | Birinchi bosishga javob ikkinchisidan keyin keladi | Deltani emas, yakuniy holatni («liked: true») yuboring; versiyalarni solishtiring |
| Erta invalidatsiya | Birinchi mutatsiyadan keyingi refetch boshqalarning optimistik ma’lumotini o‘chiradi | Kalit bo‘yicha isMutating birgacha tushganda invalidatsiya qiling |
| Ikki marta yuborish | Foydalanuvchi yana bosadi, chunki «allaqachon ishladi» | Klientda yaratilgan id yoki idempotency key, serverda deduplikatsiya |
Klientda yaratilgan ID ikki muammoni birdaniga hal qiladi: vaqtinchalik id’ni haqiqiysiga almashtirish kerak emas, bir xil id bilan takroriy so‘rovni esa backendda oson saralab tashlash mumkin.
Optimistik UI qayerda xavfli
Xato haqiqiy oqibatlarga olib keladigan joyda muvaffaqiyatni ko‘rsatmang:
- to‘lovlar, o‘tkazmalar, balansni to‘ldirish — foydalanuvchi pul ketdi deb o‘ylab, vkladkani yopib qo‘yishi mumkin;
- qoldig‘i cheklangan buyurtmalar va bronlar, bu yerda mavjudlikni server hal qiladi;
- qaytarib bo‘lmaydigan harakatlar: xat yuborish, nashr qilish, butunlay o‘chirish;
- natijasini oldindan aytib bo‘lmaydigan murakkab server validatsiyali operatsiyalar.
Bunday joylarda aniq kutish holati kerak: bloklangan tugma, progress indikatori va faqat server javobidan keyin tasdiqlash ekrani.
Ko‘p uchraydigan xatolar
- Xato haqida xabar yo‘q: element jimgina yo‘qoladi va foydalanuvchi sababini tushunmaydi.
- Parallel mutatsiyalarda butun keshni qaytarib, boshqalarning o‘zgarishlarini yo‘qotish.
- Yakuniy refetch unutilgan — klient va server bir-biridan uzoqlashadi.
- Muhim ma’lumotlarni «Bekor qilish» tugmasisiz optimistik o‘chirish.