Ibrohim.dev
Barcha yozuvlar
2 daqiqa o‘qish

Optimistik UI: foydalanuvchini kutdirmaslik san’ati

useOptimistic va Server Actions yordamida interfeysni server javobini kutmasdan yangilash: qanday ishlaydi, xatoda nima bo‘ladi va qachon ishlatmaslik kerak.

reactnextjsux

Tugmani bosdingiz, hech narsa o‘zgarmadi, yarim soniyadan keyin esa yurakcha qizardi. Shu yarim soniya ichida foydalanuvchi “bosildimi o‘zi?” deb ikkinchi marta bosib ulgurdi. Optimistik UI aynan shu bo‘shliqni yopadi: interfeys server javobini kutmaydi, natijani oldindan ko‘rsatadi.

G‘oya oddiy

So‘rovlarning aksariyati muvaffaqiyatli tugaydi. Demak, har safar spinner ko‘rsatib, kamdan-kam uchraydigan xato uchun hammani kutdirish - noto‘g‘ri savdo. Avval natijani ko‘rsatamiz, so‘rovni fonda yuboramiz, xato bo‘lsagina ortga qaytaramiz.

useOptimistic bilan

React’dagi useOptimistic hook’i buni qo‘lda state boshqarmasdan qilish imkonini beradi. U haqiqiy qiymatni oladi va transition davom etayotgan paytda vaqtinchalik qiymatni qaytaradi:

like-button.tsx
"use client";
 
import { startTransition, useOptimistic } from "react";
import { toggleLike } from "./actions";
 
type Props = { postId: string; liked: boolean; count: number };
 
export function LikeButton({ postId, liked, count }: Props) {
  const [optimistic, setOptimistic] = useOptimistic(
    { liked, count },
    (state, nextLiked: boolean) => ({
      liked: nextLiked,
      count: state.count + (nextLiked ? 1 : -1),
    }),
  );
 
  function handleClick() {
    startTransition(async () => {
      setOptimistic(!optimistic.liked);
      await toggleLike(postId);
    });
  }
 
  return (
    <button onClick={handleClick} aria-pressed={optimistic.liked}>
      {optimistic.liked ? "♥" : "♡"} {optimistic.count}
    </button>
  );
}

Server tomoni odatdagi Server Action:

actions.ts
"use server";
 
import { revalidatePath } from "next/cache";
 
export async function toggleLike(postId: string) {
  await db.like.toggle(postId);
  revalidatePath(`/blog/${postId}`);
}

revalidatePath yangi liked va count qiymatlarini props orqali qaytaradi, transition tugaydi va optimistik qiymat o‘rnini haqiqiysi egallaydi. Foydalanuvchi bu almashinuvni sezmaydi ham.

Xato bo‘lsa-chi?

Eng yoqimli joyi shu: rollback’ni o‘zingiz yozmaysiz. Action xato tashlasa, transition tugaydi va useOptimistic avtomatik ravishda haqiqiy props’ga qaytadi. Sizdan faqat foydalanuvchiga nima bo‘lganini aytish talab qilinadi:

startTransition(async () => {
  setOptimistic(!optimistic.liked);
  try {
    await toggleLike(postId);
  } catch {
    toast.error("Saqlab bo‘lmadi, qayta urinib ko‘ring");
  }
});

Jim rollback - eng yomon variant. Yurakcha o‘zi qizarib, o‘zi oqarib qolsa, foydalanuvchi buni bug deb o‘ylaydi.

Qachon ishlatmaslik kerak

Optimistik UI - va’da. Va’dani buzish qimmatga tushadigan joyda uni bermang:

  • To‘lov va buyurtma. “To‘landi” deb ko‘rsatib, keyin qaytarib olish ishonchni yo‘qotadi.
  • Qaytarib bo‘lmaydigan amallar. O‘chirish, xabar yuborish, e’lon qilish.
  • Natijasi serverga bog‘liq amallar. Agar javobda siz oldindan bilmaydigan ma’lumot (ID, hisoblangan narx, validatsiya) kelsa, taxmin qilmang.
  • Tez-tez rad etiladigan so‘rovlar. Xato ehtimoli yuqori bo‘lsa, rollback odatiy holga aylanadi.

Like, bookmark, checkbox, ro‘yxatni qayta tartiblash, izoh qo‘shish - bular esa ideal nomzodlar.

Xulosa

Tezlik faqat millisekundlar haqida emas, his haqida ham. 300 ms’lik so‘rovni 50 ms’ga tushirish qiyin, lekin uni 0 ms’dek his qildirish - bir hook’lik ish. Kichik, tez-tez takrorlanadigan va xatosi arzon amallardan boshlang: farqni birinchi bosishdayoq sezasiz.