Mundarija (27)
- Bu darsda
- 1. Nega bu kerak?
- 2. Vanilla JavaScript'da bu muammo yo'q edi
- 2.1 Bitta vazifa — bitta chizish
- 2.2 React'da qadamlar bo'linadi
- 3. Tartib: render → commit → layout effekt → chizish → effekt
- 3.1 Diagramma
- 3.2 Konsolda tekshiramiz
- 4. Miltillashni o'lchab ko'ramiz
- 4.1 Tajriba
- 4.2 Miltillaydigan variant
- 4.3 Tuzatilgan variant
- 5. Layout effekt ichida state o'zgartirish
- 5.1 Qayta render — chizishdan oldin
- 5.2 Narxi: chizish kutib turadi
- 6. Serverda (SSR) useLayoutEffect
- 6.1 Server o'lchay olmaydi
- 6.2 "Izomorf" layout effekt
- 7. useInsertionEffect — kutubxona mualliflari uchun
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): tartibni toping
- 2-mashq (o'rta): chat oxirigacha
- 3-mashq (qiyin): joy topadigan tooltip
- 10. Real ishda
- Xulosa
- Manbalar
React useLayoutEffect: ekran chizilishidan oldin o'lchash va miltillashning oldini olish
Qisqacha:
useEffectbrauzer ekranni chizgandan keyin ishlashi mumkin,useLayoutEffectesa DOM tayyor bo'lgach, lekin chizishdan oldin ishlaydi. Tooltip, popover yoki chat oxiri kabi joylarda element o'lchanadi va joyi o'zgaradi. BuniuseEffectda qilsangiz, foydalanuvchi bir-ikki kadr noto'g'ri holatni ko'radi — miltillash.useLayoutEffectbuni yo'qotadi, lekin chizishni to'xtatib turadi, shuning uchun uni faqat o'lchash uchun ishlating. Qolgan hamma joyda —useEffect.
Bu darsda
- React'da render, commit, chizish (paint) va effektlar qaysi tartibda ishlashini ayta olasiz.
- Miltillash qayerdan kelishini va uni
useLayoutEffectbilan qanday yo'qotishni tushunasiz. - Tooltip'ni o'lchab, to'g'ri joyga qo'yadigan komponent yozasiz.
useLayoutEffectnarxini va serverda (SSR) nima bo'lishini bilasiz.useInsertionEffectkim uchun ekanini ajratasiz.
Oldin bilishingiz kerak: useEffect I: tashqi tizim bilan sinxronlash, Effekt hayot sikli va StrictMode, useRef II: DOM elementlari, Element o'lchamlari va scroll.
1. Nega bu kerak?
«Bahor» menyusida taom nomi ustiga sichqonchani olib borsangiz, kichik yozuv chiqadi: "Achchiq taom". Bunday yozuv tooltip ("ishora oynasi") deb ataladi. Tooltip tugmaning tepasida chiqishi kerak: uning balandligini bilmasdan, qancha yuqoriga ko'tarishni hisoblab bo'lmaydi.
Sardor tooltip'ni useEffect bilan yozdi: effekt tooltip balandligini o'lchaydi va joyini state'ga yozadi. Kompyuterida hammasi joyida edi. Jasur aka eski telefonida ochdi: "Yozuv avval pastda chiqib, keyin tepaga sakrayapti". Bu — miltillash (flicker): foydalanuvchi bir lahzaga oraliq, noto'g'ri holatni ko'radi.
Hayotiy o'xshatish: fotosuratchi. U suratga olishdan oldin mehmonlarni joy-joyiga qo'yadi — "siz biroz chaproqqa". Agar avval suratga olib, keyin joylashtirsa, birinchi surat qiyshiq chiqadi. useLayoutEffect — "suratdan oldin joylashtirish", useEffect — "suratdan keyin".
2. Vanilla JavaScript'da bu muammo yo'q edi
2.1 Bitta vazifa — bitta chizish
Vanilla'da tooltip shunday yozilardi:
// sichqoncha kirganda — hammasi bitta ishlovchida
button.addEventListener("pointerenter", () => {
tip.hidden = false; // 1) DOM'ga qo'shdik
const { height } = tip.getBoundingClientRect(); // 2) o'lchadik
tip.style.top = `${-height - 4}px`; // 3) joyladik
});Uch qadam ham bitta vazifada (task) bajariladi. Brauzer ekranni JavaScript vazifasi tugagandan keyin chizadi (Rendering pipeline). getBoundingClientRect() esa joylashuvni o'sha zahoti hisoblatadi (Element o'lchamlari). Natijada foydalanuvchi faqat oxirgi holatni ko'radi — tooltip darhol tepada.
2.2 React'da qadamlar bo'linadi
React'da esa bu qadamlar alohida bosqichlarga taqsimlangan. Komponent avval chiziladi (render), React DOM'ni yangilaydi (commit), keyin effektlar ishlaydi. Savol: effekt brauzer ekranni chizishidan oldin ishlaydimi yoki keyin?
useEffect uchun javob: odatda keyin. React effektlarni ekran chizilishini kechiktirmaslik uchun keyinga qoldiradi. Ko'p ishlar uchun bu juda yaxshi: server so'rovi, obuna, localStorage (useEffect I) foydalanuvchini kutdirmaydi. Lekin o'lchash uchun — kech: foydalanuvchi birinchi kadrda o'lchanmagan holatni ko'rib ulguradi.
3. Tartib: render → commit → layout effekt → chizish → effekt
3.1 Diagramma
flowchart TD
R["1. Render: komponent JSX qaytaradi"] --> C["2. Commit: React DOM'ni yangilaydi"]
C --> L["3. useLayoutEffect: DOM bor, ekran hali eski"]
L --> P["4. Brauzer ekranni chizadi (paint)"]
P --> E["5. useEffect"]
L -. "setState bo'lsa — qayta render, chizishdan oldin" .-> RDiagrammadagi asosiy narsa — 3- va 4-qadamlar orasidagi chegara. useLayoutEffect DOM tayyor bo'lganda, lekin foydalanuvchi uni hali ko'rmasdan ishlaydi. Shu yerda o'lchab, state o'zgartirsangiz, React qayta chizadi — va brauzer faqat yakuniy natijani ko'rsatadi (punktir o'q).
3.2 Konsolda tekshiramiz
import { useEffect, useLayoutEffect, useRef, useState } from "react";
function App() {
const [count, setCount] = useState(0);
const button = useRef(null);
console.log("render", count);
useLayoutEffect(() => {
console.log("layout", count, button.current.textContent);
return () => console.log("layout tozalash", count);
});
useEffect(() => {
console.log("effekt", count);
return () => console.log("effekt tozalash", count);
});
return (
<button ref={button} onClick={() => setCount(count + 1)}>
+1
</button>
);
}Konsolda:
render 0
layout 0 +1
effekt 0
render 1
layout tozalash 0
layout 1 +1
effekt tozalash 0
effekt 1Ekranda:
+1Ikki narsaga qarang:
layout 0 +1— layout effekt ichidabutton.currentallaqachon tayyor, matnini o'qiy olamiz. DOM bor, ref qo'yilgan.- Yangilanishda tartib: avval layout effektning tozalashi va yangisi, keyin oddiy effektning tozalashi va yangisi. Cleanup — tozalash funksiyasi (useEffect III) — har doim o'z turidagi yangi effektdan oldin keladi.
| Qadam | count |
Nima ishladi | Ekranda |
|---|---|---|---|
| birinchi render | 0 | render, layout, (chizish), effekt | +1 |
| bosildi → render | 1 | render | hali eski kadr |
| commit | 1 | layout tozalash → layout | hali eski kadr |
| chizish | 1 | brauzer | yangi kadr |
| keyin | 1 | effekt tozalash → effekt | yangi kadr |
Tekshirib ko'ring: Ikkala effektda ham
document.title = "Savat: " + countyozish mumkin. Qaysi biri to'g'ri tanlov?
Javob
useEffect. Sahifa sarlavhasi — brauzer yorlig'idagi matn, uni o'lchash kerak emas va bir kadr kechikishi hech kimga ko'rinmaydi. useLayoutEffect faqat DOM'ni o'lchab, shu o'lchovga qarab ko'rinishni o'zgartirish kerak bo'lganda kerak.
4. Miltillashni o'lchab ko'ramiz
4.1 Tajriba
Gapga ishonmasdan, o'lchadik. Tooltip komponenti ikki variantda: effekt useEffect da va useLayoutEffect da. Sahifa haqiqiy Chrome 154.0.8037.98 da (headless) ochildi. Puppeteer sichqonchani tugma ustiga olib bordi va tooltip paydo bo'ldi. Har kadrda (requestAnimationFrame chaqiruvida) tooltip qayerda turganini yozib bordik: "pastda" (o'lchanmagan) yoki "tepada" (to'g'ri). Har variant — 10 urinish.
Ikkinchi qatorda komponent renderiga ataylab 50 ms "og'irlik" qo'shdik. Sekin telefonda yoki katta komponentda render shunday cho'ziladi.
- useEffect10 dan 8 tasida 1 kadr, 2 tasida 2 kadr1 kadr
- useLayoutEffect10 dan 10 tasida 00 kadr
- useEffect + 50 ms render10 dan 10 tasida 2 kadr2 kadr
- useLayoutEffect + 50 ms render10 dan 10 tasida 00 kadr
Manba: O'lchov 2026-10-13: Chrome 154.0.8037.98 headless, React 19.3.0, puppeteer-core hover, har kadr requestAnimationFrame'da yozildi
Grafikning ma'nosi:
useEffectbilan foydalanuvchi har safar kamida bitta kadr tooltip'ni noto'g'ri joyda ko'rdi. 60 Hz ekranda bitta kadr — taxminan 16.7 ms. Kompyuterda bu deyarli sezilmaydi, sekin telefonda esa "sakrash" bo'lib ko'rinadi.- Render og'irlashsa, xato kadrlar soni ko'paydi (2 ta).
useLayoutEffectbilan — 40 urinishning birortasida ham noto'g'ri kadr bo'lmadi.
4.2 Miltillaydigan variant
Quyidagi kodda tooltip renderiga 200 ms og'irlik qo'shilgan — farq ko'z bilan ko'rinsin. «Ishga tushir» ni bosing va «Lag'mon» ni bir necha marta bosib ko'ring: yozuv avval pastda chiqib, keyin tepaga sakraydi.
import { useEffect, useRef, useState } from "react";
function slowDown(ms) {
const start = performance.now();
while (performance.now() - start < ms) {} // ataylab sekin render
}
function Tooltip({ text }) {
const tip = useRef(null);
const [top, setTop] = useState(null); // null — hali o'lchanmagan
slowDown(200);
useEffect(() => { // ❌ chizishdan keyin
const { height } = tip.current.getBoundingClientRect();
setTop(-height - 4);
}, []);
const style = {
position: "absolute", left: 0, top: top ?? 28,
background: "#222", color: "#fff", padding: "2px 8px",
};
return <span ref={tip} role="tooltip" style={style}>{text}</span>;
}
function App() {
const [shown, setShown] = useState(false);
return (
<p style={{ position: "relative", marginTop: 40 }}>
<button onClick={() => setShown(!shown)}>Lag'mon</button>{" "}
{shown && <Tooltip text="Achchiq taom" />}
</p>
);
}Ekranda:
Lag'mon Achchiq taomtop ?? 28 — o'lchanmagan paytda tooltip tugma ostida (28 px pastda). Effekt o'lchab, tepaga ko'chiradi. Ekran matni ikkala holatda bir xil — farq faqat joylashuvda, uni ko'z bilan ko'rasiz.
4.3 Tuzatilgan variant
Bitta so'z o'zgaradi: useEffect → useLayoutEffect.
import { useLayoutEffect, useRef, useState } from "react";
function slowDown(ms) {
const start = performance.now();
while (performance.now() - start < ms) {}
}
function Tooltip({ text }) {
const tip = useRef(null);
const [top, setTop] = useState(null);
slowDown(200);
useLayoutEffect(() => { // ✅ chizishdan oldin
const { height } = tip.current.getBoundingClientRect();
setTop(-height - 4);
}, []);
const style = {
position: "absolute", left: 0, top: top ?? 28,
background: "#222", color: "#fff", padding: "2px 8px",
};
return <span ref={tip} role="tooltip" style={style}>{text}</span>;
}
function App() {
const [shown, setShown] = useState(false);
return (
<p style={{ position: "relative", marginTop: 40 }}>
<button onClick={() => setShown(!shown)}>Lag'mon</button>{" "}
{shown && <Tooltip text="Achchiq taom" />}
</p>
);
}Ekranda:
Lag'mon Achchiq taomEndi tooltip kechikib chiqadi (render sekin — 200 ms ikki marta), lekin darhol to'g'ri joyda. Foydalanuvchi uchun "biroz kech, lekin to'g'ri" — "tez, lekin sakraydigan"dan yaxshiroq. Haqiqiy loyihada slowDown bo'lmaydi va kechikish sezilmaydi.
5. Layout effekt ichida state o'zgartirish
5.1 Qayta render — chizishdan oldin
Tooltip misolida setTop layout effekt ichida chaqirildi. React bunday yangilanishni darhol, sinxron qayta ishlaydi. Tartibni o'lchov o'rniga oddiy son bilan ko'ramiz:
import { useEffect, useLayoutEffect, useState } from "react";
function App() {
const [width, setWidth] = useState(null);
console.log("render", width);
useLayoutEffect(() => {
console.log("layout");
if (width === null) setWidth(120); // "o'lchadik"
}, [width]);
useEffect(() => {
console.log("effekt", width);
}, [width]);
return <p>{width === null ? "o'lchanmoqda" : `eni: ${width}`}</p>;
}Konsolda:
render null
layout
effekt null
render 120
layout
effekt 120Ekranda:
eni: 120| Qadam | width |
Nima bo'ldi | Foydalanuvchi ko'radimi? |
|---|---|---|---|
| 1-render | null |
DOM'ga "o'lchanmoqda" yozildi | yo'q |
| layout | null → 120 |
setWidth — sinxron yangilanish |
yo'q |
| effekt | null |
birinchi commit'ning effekti | yo'q |
| 2-render, layout, effekt | 120 |
DOM'ga "eni: 120" | ha — birinchi chizilgan kadr |
Qiziq joy — effekt null. React navbatdagi sinxron renderdan oldin birinchi commit'ning kutib turgan effektlarini ham bajarib oladi. Lekin bularning hammasi chizishdan oldin sodir bo'ldi: foydalanuvchi "o'lchanmoqda" so'zini hech qachon ko'rmaydi. Buni react.dev kafolat sifatida yozadi: useLayoutEffect ichidagi kod va undagi state yangilanishlari brauzer ekranni qayta chizishidan oldin qayta ishlanadi.
5.2 Narxi: chizish kutib turadi
Bu kafolatning narxi bor. Layout effekt va undan keyingi qayta render tugamaguncha brauzer hech narsa chizmaydi. «Tuzatilgan variant» dagi misolda tooltip 400 ms kechikdi — shu vaqt ichida sahifa "qotib" turdi. Og'ir ishni (katta hisob, so'rov) layout effektga qo'ysangiz, butun ilova sekinlashadi va INP ko'rsatkichi yomonlashadi (Performance API va Core Web Vitals).
| Ish | Qaysi hook | Nega |
|---|---|---|
server so'rovi, obuna, localStorage |
useEffect |
ekranga ta'siri yo'q, kutdirmasin |
document.title |
useEffect |
bir kadr kechikish sezilmaydi |
| tooltip, popover, menyu joylashuvi | useLayoutEffect |
o'lcham → ko'rinish |
| chat oxirigacha aylantirish | useLayoutEffect |
aks holda avval yuqorida ko'rinadi |
| matn maydoni balandligini kontentga moslash | useLayoutEffect |
balandlik o'lchovga bog'liq |
| fokus qo'yish | useEffect yetadi |
joylashuv o'zgarmaydi |
Qoida bitta: avval useEffect yozing. Miltillashni ko'rsangiz (yoki o'lchov → ko'rinish zanjiri bo'lsa) — useLayoutEffect ga almashtiring.
Maslahat: react.dev
useEffecthaqida yana bir nozik joyni aytadi: effekt bosish kabi foydalanuvchi harakatidan kelib chiqsa, React uni ba'zan chizishdan oldin ham bajaradi. Bunga tayanmang — "ba'zan" kafolat emas. O'lchash kerak bo'lsa,useLayoutEffectning o'zini yozing.
Tekshirib ko'ring: Ro'yxatda 5 000 qator bor. Sardor har qatorning balandligini o'lchab, jamini state'ga yozish uchun
useLayoutEffectishlatdi. Sahifa ochilishi 1 soniyaga cho'zildi. Nima maslahat berasiz?
Javob
Layout effekt chizishni to'sib turadi — 5 000 ta o'lchov tugamaguncha foydalanuvchi bo'sh ekranni ko'radi. Yechimlar: faqat ko'rinib turgan qatorlarni chizish va o'lchash (virtualizatsiya, Uzun ro'yxatlar), yoki o'lchov natijasi birinchi kadrda shart bo'lmasa — useEffect.
6. Serverda (SSR) useLayoutEffect
6.1 Server o'lchay olmaydi
Ba'zi saytlarda HTML serverda tayyorlanadi — SSR (server-side rendering, "serverda chizish"). Next.js kabi framework'lar shunday ishlaydi — ularni keyingi qismlarda o'rganamiz. Serverda brauzer ham, ekran ham yo'q: getBoundingClientRect ni chaqirib bo'lmaydi. Shuning uchun React serverda hech qanday effektni ishga tushirmaydi — na useEffect, na useLayoutEffect.
React 19.3.0 da tekshirdik (react-dom/server, renderToString): useLayoutEffect li komponent jim chiziladi, ogohlantirish yo'q. React 18 esa shunday holatda useLayoutEffect does nothing on the server… deb ogohlantirardi — eski maqolalar va kutubxonalarda shu ogohlantirishni chetlab o'tish usullarini uchratasiz.
6.2 "Izomorf" layout effekt
Kutubxonalarda shunday qatorni ko'p ko'rasiz. Masalan, tooltip va popover joylashuvini hisoblovchi Floating UI (@floating-ui/react-dom 2.1.9, npm paket manbasidan):
var isClient = typeof document !== 'undefined';
var noop = function noop() {};
var index = isClient ? useLayoutEffect : noop;Brauzerda — useLayoutEffect, serverda — hech narsa. Boshqa kutubxonalarda serverda useEffect qo'yiladi va bunday hook useIsomorphicLayoutEffect deb ataladi. Muhimi tushuncha: serverdan kelgan HTML o'lchanmagan holatda bo'ladi. Brauzer uni ko'rsatadi, keyin React uni jonlantiradi (hydration — HTML ga hodisa ishlovchilarini ulash) va layout effekt ishlaydi. Demak SSR'da birinchi kadrdagi "o'lchanmagan" ko'rinishni o'zingiz o'ylab qo'yishingiz kerak: masalan, tooltip'ni boshida yashirin chizish. Bizning vazifalar da SSR yo'q — bu masala keyingi qismlarda.
7. useInsertionEffect — kutubxona mualliflari uchun
Uchinchi effekt hook ham bor. U layout effektlardan ham oldin ishlaydi:
import {
useEffect,
useInsertionEffect,
useLayoutEffect,
useRef,
useState,
} from "react";
function App() {
const [count, setCount] = useState(0);
const button = useRef(null);
useInsertionEffect(() => {
console.log("insertion", count, "ref:", button.current !== null);
});
useLayoutEffect(() => {
console.log("layout", count, "ref:", button.current !== null);
});
useEffect(() => console.log("effekt", count));
return (
<button ref={button} onClick={() => setCount(count + 1)}>
+1
</button>
);
}Konsolda:
insertion 0 ref: false
layout 0 ref: true
effekt 0
insertion 1 ref: true
layout 1 ref: true
effekt 1Ekranda:
+1Birinchi qatorda ref: false — insertion effekt paytida React ref'larni hali ulamagan. Bu hook CSS-in-JS kutubxonalari (styled-components, Emotion — CSS'ni JavaScript ichida yozish usuli, React'da stil berish) uchun yaratilgan. Ular <style> teglarini layout effektlar DOM'ni o'lchashidan oldin qo'shib ulgurishi kerak. Aks holda o'lchov stilsiz elementdan olinardi.
Ilova kodida useInsertionEffect deyarli kerak bo'lmaydi. Unda state o'zgartirish ham mumkin emas:
import { useInsertionEffect, useState } from "react";
function App() {
const [count, setCount] = useState(0);
useInsertionEffect(() => {
setCount(1); // ❌
}, []);
return <p>{count}</p>;
}Konsolda:
useInsertionEffect must not schedule updates.Ekranda:
1"useInsertionEffect yangilanishlarni rejalashtirmasligi kerak." Bu safar ekran o'zgardi, lekin React ogohlantirdi: bunday kodning xulqi kafolatlanmagan.
| Hook | Qachon ishlaydi | Ref tayyormi | Kim uchun |
|---|---|---|---|
useInsertionEffect |
layout effektlardan oldin | yo'q (birinchi marta) | CSS-in-JS kutubxonalari |
useLayoutEffect |
DOM tayyor, chizishdan oldin | ha | o'lchash → joylashuv |
useEffect |
odatda chizishdan keyin | ha | qolgan hamma narsa |
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| tooltip, menyu bir zum boshqa joyda chiqadi | o'lchov useEffect da |
useLayoutEffect |
| sahifa ochilishi yoki bosish sekinlashdi | og'ir ish layout effektda | useEffect ga ko'chiring yoki ishni kamaytiring |
ref.current — null |
useInsertionEffect da DOM o'qilyapti |
useLayoutEffect |
useInsertionEffect must not schedule updates. |
insertion effektda setState |
useLayoutEffect |
| serverdan kelgan sahifada element sakraydi | SSR'da effektlar ishlamaydi | boshlang'ich ko'rinishni o'ylang (yashirin chizish) |
| cheksiz qayta render | layout effektda shartsiz setState |
shart qo'ying: if (width === null) |
Oxirgi qator haqida: layout effektdagi setState ham oddiy effektdagidek qayta render beradi. Bog'liqliklar massivisiz va shartsiz yozsangiz — cheksiz sikl (useEffect II dagi tuzoq).
9. Mashqlar
Mashqlar kurs/mashqlar/17/36-layout-effekt/ da.
1-mashq (oson): tartibni toping
Kodni ishga tushirmasdan, «Keyingi» bir marta bosilgandan keyin konsolda qanday qatorlar chiqishini yozing.
import { useEffect, useLayoutEffect, useState } from "react";
function App() {
const [step, setStep] = useState(1);
useEffect(() => {
console.log("E", step);
return () => console.log("E-tozalash", step);
});
useLayoutEffect(() => {
console.log("L", step);
return () => console.log("L-tozalash", step);
});
return <button onClick={() => setStep(step + 1)}>Keyingi</button>;
}Bosilgandan keyingi birinchi qator: [:L-tozalash 1]. Oxirgi qator: [:E 2].
Yechim
Konsolda:
L 1
E 1
L-tozalash 1
L 2
E-tozalash 1
E 2Kodda useEffect yuqorida yozilgan bo'lsa ham, layout effektlar har doim oldin. Bitta turdagi effektlar esa kodda yozilgan tartibda ishlaydi.
2-mashq (o'rta): chat oxirigacha
«Bahor» qo'llab-quvvatlash chatida yangi xabar kelganda ro'yxat oxirigacha aylantiriladi. Sardor useEffect ishlatgan — yangi xabar kelganda ro'yxat bir zum yuqorida ko'rinib, keyin pastga "sakraydi". Tuzating va qo'shimcha talabni bajaring: aylantirish faqat xabarlar soni o'zgarganda bo'lsin (bog'liqlik — messages.length). Element oxiriga aylantirish — el.scrollTop = el.scrollHeight (Element o'lchamlari va scroll).
import { useEffect, useRef, useState } from "react";
function ChatLog({ messages }) {
const box = useRef(null);
useEffect(() => {
box.current.scrollTop = box.current.scrollHeight;
});
return (
<ul ref={box} style={{ height: 80, overflowY: "auto" }}>
{messages.map((m, i) => <li key={i}>{m}</li>)}
</ul>
);
}
function App() {
const [messages, setMessages] = useState(["Salom!", "Stol bormi?"]);
const add = () => setMessages([...messages, "Ha, 19:00 da"]);
return (
<div>
<ChatLog messages={messages} />
<button onClick={add}>Xabar keldi</button>
</div>
);
}Ekranda:
Salom!
Stol bormi?
Ha, 19:00 da
Xabar keldiYechim
import { useLayoutEffect, useRef, useState } from "react";
function ChatLog({ messages }) {
const box = useRef(null);
useLayoutEffect(() => {
box.current.scrollTop = box.current.scrollHeight;
}, [messages.length]);
return (
<ul ref={box} style={{ height: 80, overflowY: "auto" }}>
{messages.map((m, i) => <li key={i}>{m}</li>)}
</ul>
);
}
function App() {
const [messages, setMessages] = useState(["Salom!", "Stol bormi?"]);
const add = () => setMessages([...messages, "Ha, 19:00 da"]);
return (
<div>
<ChatLog messages={messages} />
<button onClick={add}>Xabar keldi</button>
</div>
);
}Ekranda:
Salom!
Stol bormi?
Ha, 19:00 da
Xabar keldiAylantirish — o'lchovga (scrollHeight) bog'liq ko'rinish o'zgarishi, shuning uchun layout effekt. Bog'liqlik [messages.length]: xabar matni tahrirlansa yoki boshqa sabab bilan qayta render bo'lsa, foydalanuvchi o'qiyotgan joyidan tortib ketilmaydi. Indeks key sifatida bu yerda xavfsiz: xabarlar faqat oxiriga qo'shiladi (Ro'yxatlar va key).
3-mashq (qiyin): joy topadigan tooltip
Tooltip tepada sig'masa, pastga tushsin. Avval sof funksiya yozing (React'siz sinash oson):
tooltipPlacement(anchorTop, tipHeight, gap) — anchorTop tugmaning ekran tepasidan masofasi. Tepada joy yetsa (anchorTop - tipHeight - gap >= 0), "tepada", aks holda "pastda".
Keyin Tooltip komponenti: tugma va tooltip o'lchamlarini useLayoutEffect da getBoundingClientRect() bilan oladi, natijani state'ga yozadi va data-placement atributiga chiqaradi. Birinchi renderda (o'lchanmagan) tooltip visibility: "hidden" bo'lsin — shunda SSR'da ham noto'g'ri joy ko'rinmaydi.
Yechim
import { useLayoutEffect, useRef, useState } from "react";
export function tooltipPlacement(anchorTop, tipHeight, gap) {
return anchorTop - tipHeight - gap >= 0 ? "tepada" : "pastda";
}
function Tooltip({ anchor, text }) {
const tip = useRef(null);
const [placement, setPlacement] = useState(null);
useLayoutEffect(() => {
const a = anchor.current.getBoundingClientRect();
const t = tip.current.getBoundingClientRect();
setPlacement(tooltipPlacement(a.top, t.height, 4));
}, [anchor]);
const style = {
position: "absolute", left: 0,
visibility: placement === null ? "hidden" : "visible",
...(placement === "pastda"
? { top: "100%" }
: { bottom: "100%" }),
};
return (
<span ref={tip} role="tooltip" data-placement={placement ?? ""}
style={style}>
{text}
</span>
);
}
function App() {
const button = useRef(null);
const [shown, setShown] = useState(false);
return (
<p style={{ position: "relative" }}>
<button ref={button} onClick={() => setShown(!shown)}>
Manti
</button>{" "}
{shown && <Tooltip anchor={button} text="Bug'da pishgan" />}
</p>
);
}
console.log(
tooltipPlacement(100, 30, 4),
tooltipPlacement(10, 30, 4),
);Konsolda:
tepada pastdaEkranda:
Manti Bug'da pishganHisob sof funksiyada — uni sahifasiz sinash mumkin. Komponent faqat o'lchaydi va natijani state'ga beradi. visibility: hidden — element joy egallaydi va o'lchanadi, lekin ko'rinmaydi: birinchi kadr qanday bo'lmasin, foydalanuvchi noto'g'ri joyni ko'rmaydi. Bu misolda tugma sahifa tepasida — haqiqiy brauzerda tooltip pastga tushadi.
10. Real ishda
- Floating UI (
@floating-ui/react-dom), Radix va boshqa UI kutubxonalari tooltip, popover, ochiladigan menyu joylashuviniuseLayoutEffectda hisoblaydi — biz ko'rgan izomorf qator aynan shu kutubxonadan. - Virtual ro'yxatlar (
@tanstack/react-virtualkabi) qatorlar balandligini o'lchashda layout effektga tayanadi (Uzun ro'yxatlar). - Chat va izohlar oqimi — yangi xabarda oxirigacha aylantirish, matn maydoni balandligini kontentga moslash.
- CSS-in-JS kutubxonalari
useInsertionEffectni ishlatadi; ilova dasturchisi uni deyarli yozmaydi. - Intervyu savollari: "
useEffectvauseLayoutEffectfarqi nima?", "QachonuseLayoutEffectishlatasiz?", "Nega hamma joydauseLayoutEffectyozib qo'ya qolmaymiz?", "SSR'da layout effekt nima bo'ladi?".
Xulosa
- Tartib: render → commit (DOM) →
useLayoutEffect→ brauzer chizadi →useEffect. useEffectdagi o'lchov foydalanuvchiga noto'g'ri kadrni ko'rsatishi mumkin — o'lchovimizda har safar 1–2 kadr.useLayoutEffectdagisetStatechizishdan oldin qayta ishlanadi — miltillash yo'q.- Narxi: layout effekt chizishni to'sadi; og'ir ishni u yerga qo'ymang. Avval
useEffect. - Serverda effektlar ishlamaydi; React 19.3 ogohlantirmaydi, lekin birinchi ko'rinishni o'zingiz o'ylang.
useInsertionEffect— CSS-in-JS kutubxonalari uchun: ref yo'q,setStatemumkin emas.
Keyingi dars: useId: barqaror va noyob id — label va maydonni, xato matnini bog'lash uchun komponent qayta ishlatilsa ham to'qnashmaydigan id yasaymiz.
Manbalar
- React:
useLayoutEffect— react.dev/reference/react/useLayoutEffect - React:
useInsertionEffect— react.dev/reference/react/useInsertionEffect - React:
useEffect(Caveats — bosishdan kelgan effektlar) — react.dev/reference/react/useEffect - Floating UI:
@floating-ui/react-dom2.1.9 (npm, 2026-10-13 holati) — floating-ui.com
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!