useLayoutEffect Hook
useLayoutEffect Hook — Rendering-ից առաջ DOM-ի սինխրոն թարմացում
useLayoutEffect-ը React-ի hook է, որը շատ նման է useEffect-ին, բայց այն գործարկվում է սինխրոն կերպով՝ անմիջապես render-ից հետո և մինչև DOM-ի repaint-ը։ Այսինքն՝ այն կատարվում է այն պահին, երբ React-ը արդեն թարմացրել է DOM-ը, բայց դեռ չի “ցուցադրել” այն օգտվողին։
Սա նշանակում է, որ կարող ես ապահով կերպով չափել DOM-ի չափերը, scroll-ի դիրքը, կամ պատրաստել layout animation՝ առանց աչքի տեսանելի թարթման։
| useEffect | useLayoutEffect |
|---|---|
| Ասինխրոն է․ աշխատում է render-ից հետո, երբ DOM-ը արդեն տեսանելի է։ | Սինխրոն է․ աշխատում է render-ից անմիջապես հետո, բայց մինչև repaint-ը։ |
| Հարմար է side effect-ների (fetch, event, subscription) համար։ | Հարմար է layout effect-ների (DOM չափում, scroll, animation) համար։ |
| Չի արգելակում UI-ի rendering-ը։ | Կարող է արգելակել rendering-ը, եթե երկար computations կան։ |
Երբ ուզում ես իմանալ որևէ էլեմենտի բարձրությունը կամ լայնությունը՝ ճիշտ ժամանակը useLayoutEffect-ն է։ Եթե նույնը անես useEffect-ով, այն կարող է հաշվարկել արդեն տեսանելի DOM-ը՝ առաջացնելով թարթում (flicker)։
import React, { useRef, useLayoutEffect, useState } from 'react';
export default function BoxMeasure() {
const boxRef = useRef();
const [boxHeight, setBoxHeight] = useState(0);
useLayoutEffect(() => {
const height = boxRef.current.getBoundingClientRect().height;
setBoxHeight(height);
}, []);
return (
<div>
<div
ref={boxRef}
style={{ width: '200px', height: '150px', background: '#61dafb', marginBottom: '10px' }}
></div>
<p>Բլոկի բարձրությունը՝ {boxHeight}px</p>
</div>
);
}
Այստեղ `useLayoutEffect`-ը թույլ է տալիս ճիշտ չափել DOM-ի չափսը՝ նախքան React-ը այն պատկերում է էկրանին։ Եթե նույնը անեիր `useEffect`-ով, սկզբում կտեսնեիր “0px” և հետո նոր թարմացում։
Եթե ցանկանաս վերականգնել scroll-ի դիրքը render-ից անմիջապես հետո, պետք է օգտագործես `useLayoutEffect`, քանի որ scroll-ը պետք է իրականացվի նախքան repaint-ը։
import React, { useLayoutEffect } from 'react';
export default function ScrollTop() {
useLayoutEffect(() => {
window.scrollTo(0, 0);
}, []);
return (
<div style={{ height: '200vh', background: 'linear-gradient(#fff, #ccc)' }}>
<h2>Էջը վերև է տեղափոխվում render-ից անմիջապես հետո</h2>
</div>
);
}
Այս օրինակով էջը scroll է անում հենց render-ից հետո, առանց աչքի տեսանելի շարժման։
Երբ պետք է մի քանի DOM էլեմենտ reposition անես կամ անիմացիա պատրաստես, կարող ես օգտվել useLayoutEffect-ից, որպեսզի չափես նախորդ և ընթացիկ դիրքերը։
import React, { useState, useLayoutEffect, useRef } from 'react';
export default function AnimatedBox() {
const [toggle, setToggle] = useState(false);
const boxRef = useRef();
useLayoutEffect(() => {
const box = boxRef.current;
box.style.transition = 'none';
box.style.transform = 'scale(0.5)';
requestAnimationFrame(() => {
box.style.transition = 'transform 0.4s ease';
box.style.transform = 'scale(1)';
});
}, [toggle]);
return (
<div>
<button onClick={() => setToggle(t => !t)}>Փոխել դիրքը</button>
<div
ref={boxRef}
style={{
marginTop: '20px',
width: '100px',
height: '100px',
background: toggle ? '#4caf50' : '#ff5722',
}}
></div>
</div>
);
}
Այս օրինակով `useLayoutEffect`-ը ապահովում է անխափան անիմացիա՝ առանց "flicker"-ի։ Եթե նույնը անես `useEffect`-ով՝ React-ը կպատկերեր անցումային վիճակը։
SSR-ի դեպքում օգտագործիր՝
if (typeof window !== 'undefined') { useLayoutEffect(...); }
Գրիր կոմպոնենտ, որը․