Mundarija (24)
- Bu darsda
- 1. Nega bu kerak?
- 2. sonner: bitta <Toaster />, istalgan joydan toast()
- 2.1 Birinchi toast
- 2.2 toast.promise: yuklanish → natija
- 2.3 action: «Bekor qilish»
- 3. Toast va ekran o'quvchi
- 3.1 sonner ichida nima bor
- 3.2 Asosiy qoida: hudud oldindan bo'lsin
- 4. Toast, inline xato yoki modal?
- 5. Boshqa toast kutubxonalari
- 5.1 react-hot-toast
- 5.2 react-toastify
- 5.3 sonner'ning <style> i — natija oynasida ko'ramiz
- 6. Biz sinab ko'rdik: nega vazifalar da sonner yo'q
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): qaysi biri?
- 2-mashq (o'rta): toast.promise bilan saqlash
- 3-mashq (qiyin): o'z toast'ingizga «Bekor qilish»
- 4-mashq: tajriba qadami (ixtiyoriy)
- 9. Real ishda
- Xulosa
- Manbalar
Toast bildirishnomalar React'da: sonner, aria-live va qachon toast kerak emas
Qisqacha: Toast — ekran burchagida bir necha soniya ko'rinib, o'zi yo'qoladigan qisqa xabar: "Saqlandi", "Nusxa olindi". React'da eng ommabopi — sonner: ildizga bitta
<Toaster />, istalgan joydantoast.success("…"). Toast ishni to'xtatmaydi — shuning uchun forma xatosi uchun (inline xato kerak) va xavfli amal tasdig'i uchun (modal kerak) u noto'g'ri tanlov. Ekran o'quvchi uchun toast —aria-livehudud. sonner o'z CSS'ini<style>tegi bilan qo'shadi, shuning uchunvazifalaro'z toast'ida qoldi.
Bu darsda
- sonner bilan success, error,
toast.promiseva «Bekor qilish» (action) li toastlarni jonli yasaysiz. - Toast ekran o'quvchiga qanday yetishini (
aria-live,role="status") va klaviatura bilan unga qanday borilishini ko'rasiz. - Toast, inline xato va modal oyna orasidagi chegarani aniq qoidalar bilan aytasiz.
- react-hot-toast va react-toastify'ni bilasiz, ularning CSP bilan munosabatini o'lchov va manba kodidan o'qiysiz.
vazifalarnega sonner emas, o'z toast'ini ishlatishini kanon raqamlari bilan tushuntirasiz.
Oldin bilishingiz kerak: Jonli hududlar va dinamik kontent, Tooltip, dropdown va toast bildirishnomalar, Portallar, shadcn/ui II.
1. Nega bu kerak?
Jasur aka kassada buyurtma saqladi. Ekranda hech narsa o'zgarmadi — saqlandimi yoki yo'qmi? U yana bosdi. Ikki buyurtma bo'ldi.
Har amaldan keyin foydalanuvchi javob kutadi. Ba'zi javoblar sahifaning o'zida ko'rinadi (ro'yxatga yangi qator qo'shildi). Ba'zilari esa ko'rinmaydi: "server saqladi", "havola nusxalandi", "fon rejimida yuborildi". Ana shular uchun toast.
CSS toast darsida toast'ni popover bilan qurgan edik, Portallar darsida esa vazifalar ga o'z toast'imiz — Bildirishnoma qo'shilgan. Bugun React ekotizimidagi tayyor yechimlarni ko'ramiz va eng muhim savolga javob beramiz: toast qachon kerak.
Hayotiy o'xshatish: toast — ofitsiantning "buyurtmangiz oshxonaga ketdi" degan qisqa gapi. U sizni to'xtatmaydi, javob kutmaydi. Agar sizdan javob kerak bo'lsa ("osh tugagan, manti olasizmi?"), ofitsiant to'xtab, savol beradi — bu endi modal oyna.
2. sonner: bitta <Toaster />, istalgan joydan toast()
2.1 Birinchi toast
sonner (2.0.8) — shadcn/ui'ning toast komponenti aynan shu (shadcn/ui II dagi Sonner). Ishlash tartibi ikki qadam:
- Ilova ildiziga bitta
<Toaster />— toastlar shu yerda chiziladi. - Istalgan joydan —
toast("matn"), hatto komponentdan tashqarida ham.
Natija oynasida sonner bor:
import { Toaster, toast } from "sonner";
function showError() {
toast.error("Server javob bermadi", {
description: "Qayta urinib ko'ring",
});
}
export default function App() {
return (
<div>
<button onClick={() => toast.success("Bron saqlandi")}>
Saqlash
</button>{" "}
<button onClick={showError}>Xato</button>
<Toaster richColors />
</div>
);
}Ekranda:
Saqlash Xato
Server javob bermadi
Qayta urinib ko'ring
Bron saqlandi«Ishga tushir» bilan bosib ko'ring: toastlar o'ng pastda ustma-ust chiqadi, bir necha soniyadan keyin yo'qoladi. Ekran matnida tartibga qarang: yangisi tepada — sonner oxirgi toast'ni ro'yxat boshiga qo'yadi.
toast.success,toast.error— turi (ikonka va rang). Yanatoast.info,toast.warning,toast.loadingva oddiytoast().description— sarlavha ostidagi ikkinchi qator.richColors— success yashil, error qizil fonda. Bermasangiz — hammasi bir xil oq fon, farq faqat ikonkada.
<Toaster /> ning boshqa sozlamalari: position ("top-center", "bottom-right"…), duration (ms, standart 4000), closeButton, expand (ustma-ust emas, ochiq ro'yxat), theme ("light", "dark", "system").
2.2 toast.promise: yuklanish → natija
Server so'rovi uchun eng qulay shakl. Bitta toast holatini o'zi almashtiradi:
import { Toaster, toast } from "sonner";
// Soxta server: 300 ms dan keyin buyurtma raqami
const sendOrder = () =>
new Promise((resolve) => {
setTimeout(() => resolve({ id: 12 }), 300);
});
function submit() {
toast.promise(sendOrder(), {
loading: "Yuborilmoqda…",
success: (order) => `Buyurtma №${order.id} qabul qilindi`,
error: "Yuborib bo'lmadi",
});
}
export default function App() {
return (
<div>
<button onClick={submit}>Yuborish</button>
<Toaster />
</div>
);
}Ekranda:
Yuborish
Buyurtma №12 qabul qilindiBosilgan zahoti toast «Yuborilmoqda…» edi (bu oraliq holatni biz ham tekshirdik). Promise bajarilgach o'sha toast muvaffaqiyat matniga almashdi. success — funksiya: promise natijasini (order) oladi. Promise rad etilsa (reject), error matni chiqadi (Promise asoslari darsidagi uch holat — kutish, bajarildi, rad etildi — toast ko'rinishida).
2.3 action: «Bekor qilish»
O'chirishdan oldin "ishonchingiz komilmi?" deb so'rash o'rniga — darhol o'chirib, toast'da «Bekor qilish» tugmasini berish. Gmail "Xat o'chirildi. Bekor qilish" shunday ishlaydi.
import { useState } from "react";
import { Toaster, toast } from "sonner";
export default function App() {
const [items, setItems] = useState(["Osh", "Manti"]);
function remove() {
setItems(["Manti"]);
toast("Osh o'chirildi", {
action: {
label: "Bekor qilish",
onClick: () => setItems(["Osh", "Manti"]),
},
});
}
return (
<div>
<p>{items.join(", ")}</p>
<button onClick={remove}>O'chirish</button>
<Toaster />
</div>
);
}Ekranda:
Osh, Manti
O'chirishBosh holat qaytdi. Avval «O'chirish»: Osh yo'qoldi, toast chiqdi. Keyin klaviatura bilan «Bekor qilish»: Osh qaytdi, toast yopildi. Klaviatura yo'lini o'zingiz sinang — bu Bobur (faqat klaviatura) yo'li:
| Qadam | Fokus | Ekranda |
|---|---|---|
| «O'chirish» | «O'chirish» tugmasi | «Manti», toast «Osh o'chirildi» |
| Tab | toast'ning o'zi (sonner har toast'ga tabindex="0" beradi) |
o'zgarmaydi |
| Tab | «Bekor qilish» | o'zgarmaydi |
| Enter | — | «Osh, Manti», toast yopildi |
<Toaster /> DOM'da tugmadan keyin turgani uchun Tab unga yetdi.
Diqqat: Toast'dagi tugma bilan ikki muammo bor. Birinchisi — vaqt. Toast 4 soniyada yo'qoladi: sekin o'qiydigan yoki klaviatura bilan yuradigan odam ulgurmasligi mumkin. sonner sichqoncha ustida turganda va fokus toast ichida bo'lganda taymerni to'xtatadi, lekin bunga yetib borish kerak. Ikkinchisi — joy: toast DOM oxirida, Bobur unga uzoq Tab bosib boradi. sonner'da buning uchun yorliq bor —
Alt+T(quyidagiaria-labelda ko'rasiz). Shuning uchun bekor qilish imkoniyati faqat toast'da qolmasin: masalan, "O'chirilganlar" bo'limi ham bo'lsin.
3. Toast va ekran o'quvchi
3.1 sonner ichida nima bor
Toast ko'z uchun burchakda. Malika esa fokusdagi elementni eshitadi — burchakdagi yangi matnni u qanday biladi? Jonli hududlar darsidagi javob: aria-live. sonner'ning DOM'ini o'qiymiz:
import { Toaster, toast } from "sonner";
function inspect() {
setTimeout(() => {
const region = document.querySelector("section");
for (const name of ["aria-live", "aria-label", "aria-relevant"]) {
console.log(name, "=", region.getAttribute(name));
}
const item = document.querySelector("[data-sonner-toast]");
console.log("toast tabindex =", item.getAttribute("tabindex"));
}, 200);
}
export default function App() {
return (
<div>
<button
onClick={() => {
toast.success("Saqlandi");
inspect();
}}
>
Saqlash
</button>
<Toaster />
</div>
);
}Konsolda:
aria-live = polite
aria-label = Notifications alt+T
aria-relevant = additions text
toast tabindex = 0aria-live="polite"— yangi toast matni ekran o'quvchida joriy gap tugagach o'qiladi. Muhim:toast.errorhampolite— sonner xatoni ham shoshiltirmaydi.aria-label="Notifications alt+T"— hudud nomi va yorliq: Alt+T toastlar ro'yxatiga fokusni olib boradi. Nom inglizcha — o'zbekcha ilovada<Toaster containerAriaLabel="Bildirishnomalar" />bilan almashtiring.aria-relevant="additions text"— faqat qo'shilgan toastlar va matn o'zgarishi o'qiladi, yo'qolganlari emas.tabindex="0"— har toast Tab bilan fokus oladi (oldingi bo'limdagi Bobur yo'li).
3.2 Asosiy qoida: hudud oldindan bo'lsin
Jonli hududlar darsidagi qoida React'da ayniqsa muhim: jonli hudud DOM'da xabardan oldin bo'lishi kerak. Ekran o'quvchi mavjud hududdagi o'zgarishni kuzatadi. Agar hudud xabar bilan birga paydo bo'lsa, ko'p ekran o'quvchi uni o'qimaydi.
React'da ko'p uchraydigan xato:
{message && <p role="status">{message}</p>}message bo'sh bo'lganda <p> yo'q. Xabar kelganda <p> matni bilan birga tug'iladi — hudud yangi, kuzatilmagan.
Hudud doim turadi, faqat matni o'zgaradi:
<p role="status">{message}</p>sonner'da <section aria-live> <Toaster /> chizilganda paydo bo'ladi — birinchi toast'dan oldin. Kanon Bildirishnoma ham shunday: .bildirishnoma:empty { opacity: 0; } — bo'sh bo'lsa ko'rinmaydi, lekin DOM'da.
Tekshirib ko'ring: Nega bo'sh toast'ni
display: noneyokihiddenbilan yashirmaymiz,opacity: 0bilan yashiramiz?
Javob
display: none va hidden elementni ekran o'quvchining daraxtidan ham olib tashlaydi. Keyin matn bilan birga yana ko'rinsa — bu xuddi "yangi tug'ilgan" hudud, o'zgarish o'qilmasligi mumkin. opacity: 0 faqat ko'zdan yashiradi: element daraxtda qoladi va kuzatiladi. Kanon CSS izohi ham shuni aytadi: "hidden yoki display: none bo'lsa, ekran o'quvchi keyingi matnni o'qimasligi mumkin".
4. Toast, inline xato yoki modal?
Bu darsning eng muhim qismi. Toast — bloklamaydigan, o'tkinchi, ixtiyoriy xabar. Shu uch so'z chegarani belgilaydi:
flowchart TD
A{"Foydalanuvchidan<br/>javob kerakmi?"} -->|ha| M["Modal / dialog"]
A -->|yo'q| B{"Xabar aniq bir<br/>maydonga tegishlimi?"}
B -->|ha| I["Inline xato<br/>maydon yonida"]
B -->|yo'q| C{"Uni o'tkazib yuborsa<br/>ziyon bormi?"}
C -->|ha| P["Sahifadagi doimiy xabar<br/>(banner)"]
C -->|yo'q| T["Toast"]Nimaga qarang: toast — daraxtning eng oxirgi bargi. Avval boshqa uch savoldan o'tadi.
| Vaziyat | To'g'ri | Nega toast emas |
|---|---|---|
| Telefon raqami formati xato | inline xato (aria-describedby) |
xato maydonga tegishli; toast 4 s da yo'qoladi, odam qaysi maydonni tuzatishni unutadi |
| "Vazifa o'chirilsinmi?" | modal / <dialog> |
javob kerak |
| Internet uzildi | doimiy banner | holat davom etadi, o'tkinchi emas |
| "Bron saqlandi" | toast | natija tasdig'i, javob kerak emas |
| "Havola nusxalandi" | toast | ko'rinmas amalning tasdig'i |
| O'chirildi + bekor qilish | toast + action (va boshqa yo'l ham) |
qaytariladigan amal |
Ko'p uchraydigan xato — "hamma xato toast'da". Forma yuborildi, server 5 ta maydonda xato qaytardi, ekranda 5 ta qizil toast. Ular yo'qoladi, Aziz esa qaysi maydon ekanini bilmay qoladi. To'g'risi — har xato o'z maydoni yonida (RHF + Zod darsidagi aria-invalid va aria-describedby), toast esa bitta umumiy gap bilan: "Formada 5 ta xato bor".
Tekshirib ko'ring: "Sessiya muddati tugadi, qaytadan kiring" — toast'mi?
Javob
Yo'q. Foydalanuvchi bundan keyin ishlay olmaydi — amal kerak (qayta kirish). Toast yo'qolsa, u nega hech narsa saqlanmayotganini tushunmaydi. To'g'risi — modal oyna (yoki kirish sahifasiga yo'naltirish) aniq tugma bilan. "O'tkazib yuborsa ziyon bormi?" savoliga javob — ha.
5. Boshqa toast kutubxonalari
5.1 react-hot-toast
react-hot-toast (2.6.1) — kichik, API'si sonner'ga o'xshaydi (toast.success, toast.promise, <Toaster />). Uslubini goober (kichik CSS-in-JS) bilan beradi. Kanon tajribasida (kitlar-ishlat-toast-sonner-toast-hot.txt) — native <dialog> + react-hot-toast varianti vazifalar CSP'si ostida:
{"v":"toast-hot","xato":"Waiting failed: 10000ms exceeded","csp":["require-trusted-types-for trusted-types-sink"],"konsol":["This document requires 'TrustedHTML' assignment. The action has been blocked.","pageerror: Failed to set the 'innerHTML' property on 'Element': This document requires 'TrustedHTML' assignment.\nObject.assign (<anonymous>)"]}Ilova ochilmadi: goober uslubni innerHTML bilan yozadi, Trusted Types to'sadi — Ant Design va Mantine bilan bir xil sabab. Kanon raqami (kanon qaydi, 4-qaror): +4.5 KB.
5.2 react-toastify
react-toastify (11.1.0) — eng eski va ko'p sozlamali kutubxonalardan biri: toast.success, <ToastContainer />, progress chizig'i, sudrab yopish. Kanon o'lchamagan. Paketni o'rnatib, ichini ko'rdik (2026-10-17):
- standart kirish (
react-toastify) — CSS'nidocument.createElement("style")bilan o'zi qo'shadi (dist/index.mjsda bitta shunday joy); react-toastify/unstyled— xuddi shu komponentlar, lekin<style>yasamaydi (unstyled.jsda 0 ta), CSS esa alohida faylda:react-toastify/dist/ReactToastify.css.
Ya'ni CSP'li loyiha uchun yo'l bor: unstyled kirish + CSS faylni import qilish. Bu "kutubxona sizning siyosatingizga moslashadi" tamoyilining yaxshi namunasi. Baribir — o'z ilovangizda CSP ostida sinamaguncha "ishlaydi" demang.
5.3 sonner'ning <style> i — natija oynasida ko'ramiz
sonner'chi? Natija oynasida ikki blokni solishtiramiz. Avval faqat React:
const count = document.querySelectorAll("style").length;
console.log("<style>:", count);
export default function App() {
return <p>Faqat React</p>;
}Konsolda:
<style>: 1Bittasi — natija oynasining o'z uslubi. Endi sonner import qilingan blok:
import { Toaster } from "sonner";
const styles = [...document.querySelectorAll("style")];
console.log("<style>:", styles.length);
console.log(styles[1].textContent.slice(0, 31));
export default function App() {
return <Toaster />;
}Konsolda:
<style>: 2
[data-sonner-toaster][dir=ltr],Ikkinchi teg — sonner'ning CSS'i: u import paytida JavaScript bilan <style> yasab, <head> ga qo'shadi. Bitta ham toast ko'rsatilmasdan. Natija oynasida inline <style> ga ruxsat bor — ishladi. vazifalar ning style-src 'self' siyosatida esa bu teg to'siladi: kanon o'lchovida 2 ta buzilish (style-src-elem inline ×2), toast chiqdi, lekin uslubsiz.
Maslahat: Natija oynasida Radix yoki lucide import qilingan bloklarda ham
<style>2 ta bo'ladi — Radix, cmdk, sonner, lucide, clsx va cva natija oynasiga bitta to'plamda yuklanadi — sonner'ning CSS'i ham birga keladi. Shuning uchun Radix chuqur darsida "oldin" qiymati 2 edi.
6. Biz sinab ko'rdik: nega vazifalar da sonner yo'q
Bu darsda vazifalar kodi o'zgarmaydi — o'lchov darsi. Kanon tasdiq oynasi + toast ekranini uch xil toast bilan o'lchadi (Chrome 155, vazifalar CSP'si):
| Variant | Hajm (kanon qaydi) | CSP | Natija |
|---|---|---|---|
| o'z toast (kanon) | 0 KB | 0 | |
| sonner 2.0.8 | +8.9 KB | 2 (style-src-elem) |
toast chiqdi, uslub to'sildi |
| react-hot-toast 2.6.1 | +4.5 KB | Trusted Types | ochilmadi |
Faqat React'ga nisbatan o'lchovda (kutubxonalar.txt) sonner — +9.6 KB va o'sha 2 buzilish.
Kanon qarori (20-qism qarorlari, 2-qaror): "Tasdiq oynasi native <dialog>, toast — o'zimizniki — QABUL. Sabab CSP". Bizning toast — 18-qismdan beri bor (Portallar):
// Bildirishnoma.tsx (kanon, qisqartirilgan)
export function Bildirishnoma({ matn }: Props) {
// role="status" — jonli hudud doim DOM'da (bo'sh bo'lsa ham)
return createPortal(
<p id="elon" role="status" className="bildirishnoma">
{matn}
</p>,
document.body,
);
}Matn 3 soniyadan keyin o'chadi (ilova-holati.ts, ELON_MUDDATI = 3000, Zustand store'da). CSS — oddiy fayl, position: fixed, pointer-events: none (18-qismda e2e topgan: toast ostidagi tugmani yopib qo'ygan edi).
Nima yo'qotdik? sonner'ning ustma-ust toastlari, toast.promise, action, sudrab yopish, chiroyli animatsiya. vazifalar ga hozircha bular kerak emas: e'lonlar qisqa, bittadan. Kerak bo'lsa — o'zimiz qo'shamiz (3-mashqda «Bekor qilish» ni yozasiz) yoki sonner'ni CSP'ga mos yo'l bilan ulaymiz.
sonner qachon to'g'ri tanlov: shadcn loyihasi, CSP'siz yoki serverli nonce bilan; bir nechta toast ustma-ust, promise va action kerak bo'lganda. Bu holatlarda sonner — eng qulay variant.
7. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| toast chiqmadi, xato yo'q | <Toaster /> ilovada yo'q |
ildizga bitta <Toaster /> |
| har toast ikki marta | ikkita <Toaster /> (layout va sahifada) |
faqat bittasi |
| ekran o'quvchi xabarni o'qimaydi | hudud xabar bilan birga tug'iladi | hudud doim DOM'da, matn o'zgaradi |
| forma xatolari toast'da yo'qoladi | xato maydonga tegishli | inline xato + aria-describedby |
Applying inline style violates … 'style-src 'self'' |
sonner <style> qo'shadi |
CSP'ga mos variant yoki o'z toast |
This document requires 'TrustedHTML' assignment |
react-hot-toast (goober) innerHTML |
boshqa kutubxona |
| toast tugmani yopib qo'ydi, bosilmaydi | toast pointer-events |
tugmasiz toast'ga pointer-events: none |
8. Mashqlar
Mashqlar kurs/mashqlar/20/09-toast/ da.
1-mashq (oson): qaysi biri?
Har vaziyatga bitta so'z: toast, inline, modal yoki banner.
- Parol 8 belgidan qisqa:
- "Rasm yuklandi":
- "Butun menyu o'chirilsinmi?":
- Server texnik ishlar sababli 30 daqiqa yopiq:
Yechim
- Parol — maydonga tegishli xato:
inline, maydon ostida,aria-describedbybilan. - "Rasm yuklandi" — javob kerak emas, o'tkazib yuborsa ziyon yo'q:
toast. - Butun menyuni o'chirish — javob kerak, xavfli:
modal. - Texnik ishlar — holat davom etadi:
banner, sahifada doimiy.
2-mashq (o'rta): toast.promise bilan saqlash
«Saqlash» tugmasi soxta server funksiyasini chaqiradi. Toast: yuklanishda «Saqlanmoqda…», muvaffaqiyatda «Taom saqlandi: Manti», xatoda «Saqlab bo'lmadi». Server 300 ms kutadi va taom nomini qaytaradi. Ekranda oxirida tugma va toast qolsin.
Ishora: «toast.promise» bo'limi; success — funksiya.
Saqlash
Taom saqlandi: MantiYechim
import { Toaster, toast } from "sonner";
const saveDish = (name) =>
new Promise((resolve) => setTimeout(() => resolve(name), 300));
function save() {
toast.promise(saveDish("Manti"), {
loading: "Saqlanmoqda…",
success: (name) => `Taom saqlandi: ${name}`,
error: "Saqlab bo'lmadi",
});
}
export default function App() {
return (
<div>
<button onClick={save}>Saqlash</button>
<Toaster />
</div>
);
}Ekranda:
Saqlash
Taom saqlandi: MantiOraliq holat — «Saqlanmoqda…» — bosilgan zahoti ko'rinadi. saveDish ni reject qiladigan qilib o'zgartirib ko'ring: toast «Saqlab bo'lmadi» bo'ladi. Haqiqiy loyihada bu yerda mutatsiya bo'lardi va toast.promise ga uning promise'i beriladi.
3-mashq (qiyin): o'z toast'ingizga «Bekor qilish»
Kanon Bildirishnoma ruhida, kutubxonasiz:
Toastkomponenti —role="status"li<div>doim DOM'da; ichida matn va (bo'lsa) «Bekor qilish» tugmasi.- «O'chirish» — ro'yxatdan «Osh» ni olib tashlaydi va toast'ga «Osh o'chirildi» + bekor qilish amalini beradi.
- «Bekor qilish» — ro'yxatni tiklaydi va toast'ni bo'shatadi.
- Toast 3 000 ms dan keyin o'zi bo'shaydi (taymer —
useEffect, cleanup bilan).
O'chirib, bekor qilgandan keyin ekran:
Osh, Manti
O'chirishIshora: holat { text, undo } yoki null; taymer — effekt va cleanup.
Yechim
import { useEffect, useState } from "react";
function Toast({ toast, onClose }) {
useEffect(() => {
if (toast === null) return;
const timer = setTimeout(onClose, 3000);
return () => clearTimeout(timer);
}, [toast, onClose]);
return (
<div role="status" className="toast">
{toast !== null && (
<>
{toast.text}{" "}
{toast.undo && (
<button
onClick={() => {
toast.undo();
onClose();
}}
>
Bekor qilish
</button>
)}
</>
)}
</div>
);
}
export default function App() {
const [items, setItems] = useState(["Osh", "Manti"]);
const [toast, setToast] = useState(null);
function remove() {
const before = items;
setItems(items.filter((i) => i !== "Osh"));
const undo = () => setItems(before);
setToast({ text: "Osh o'chirildi", undo });
}
return (
<div>
<p>{items.join(", ")}</p>
<button onClick={remove}>O'chirish</button>
<Toast toast={toast} onClose={() => setToast(null)} />
</div>
);
}Ekranda:
Osh, Manti
O'chirishrole="status" li <div> doim bor — faqat ichi o'zgaradi («Hudud oldindan bo'lsin»). before — o'chirishdan oldingi ro'yxat, closure ichida saqlanadi (Closure). Taymer har yangi toast'da qayta boshlanadi: toast o'zgarganda effekt cleanup eski taymerni to'xtatadi — aks holda eski taymer yangi toast'ni muddatidan oldin yopardi (eskirgan closure va effekt cleanup). Bir kamchilik: onClose har renderda yangi funksiya — effekt har renderda qayta ulanadi. Kanondagi kabi useEffectEvent (Effekt hodisalari) yoki useCallback bilan tuzatish mumkin.
4-mashq: tajriba qadami (ixtiyoriy)
vazifalar ga tegmang. kurs/tajriba/toast/ da Vite + React loyiha: bitta tugma va sonner. index.html ga vazifalar dagi CSP <meta> ini qo'shing, DevTools konsolida buzilishlarni sanang. Keyin <Toaster /> ni olib tashlab (faqat import { toast } from "sonner" qoldirib) yana sanang.
Yechim
<Toaster />bilan: konsoldaApplying inline style violates the following Content Security Policy directive 'style-src 'self''…— kanon o'lchovidagi 2 ta buzilishga o'xshash natija; toast chiqadi, lekin joylashuvi va ko'rinishi buzilgan.<Toaster />siz ham buzilish qoladi: sonner CSS'ni import paytida qo'shadi («sonner'ning<style>i» bo'limi), toast ko'rsatilishini kutmaydi.- Xulosa yozing: "sonner — import qilingan zahoti
<style>yasaydi;style-src 'self'bilan ishlashi uchun nonce yoki boshqa yechim kerak".
9. Real ishda
- shadcn loyihalari —
sonnerstandart:components/ui/sonner.tsxo'rami, ildizda<Toaster />. Server javoblari uchuntoast.promiseyoki TanStack Query'ningonSuccess/onErrorichidatoast. - Kod ko'rigi — "bu xato toast'da bo'lishi kerakmi?" degan savol tez-tez beriladi. Javob — «Toast, inline xato yoki modal?» bo'limidagi daraxt.
- Intervyu savollari: "toast va modal farqi?", "
aria-livepolitevaassertiveqachon?", "nega jonli hudud oldindan DOM'da bo'lishi kerak?", "kutubxona CSP bilan ishlashini qanday tekshirasiz?".
Xulosa
- Toast — bloklamaydigan, o'tkinchi, ixtiyoriy xabar; javob kerak bo'lsa — modal, maydon xatosi — inline, davomli holat — banner.
- sonner: ildizda bitta
<Toaster />, istalgan joydantoast.success/error/promise,actionbilan «Bekor qilish»,richColors,position. - Ekran o'quvchi uchun —
aria-live="polite"hudud; hudud xabardan oldin DOM'da bo'lsin (opacity: 0,display: noneemas). - sonner CSS'ni import paytida
<style>bilan qo'shadi (vazifalarCSP'sida 2 buzilish), react-hot-toast Trusted Types'da ochilmaydi, react-toastify'daunstyled+ CSS fayl yo'li bor. vazifalar— o'z toast'i:role="status", portal, 3 s,pointer-events: none.
Keyingi dars: Modal, drawer, popover va tooltip — qatlamli UI'ning to'rt turi: native <dialog> va Radix Dialog, vaul drawer, Floating UI'ning offset/flip/shift i, URL bilan modal — va vazifalar ga Radix tooltip.
Manbalar
- sonner — sonner.emilkowal.ski (2.0.8): Toaster, toast.promise, action
- react-hot-toast — react-hot-toast.com (2.6.1); react-toastify — fkhadra.github.io/react-toastify (11.1.0)
- WAI-ARIA:
aria-live,role="status"— w3.org/TR/wai-aria - Nielsen Norman Group: Indicators, Validations, and Notifications — nngroup.com
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!