useEffect Hook
useEffect Hook — Կողային էֆեկտների կառավարում React-ում
React-ում բոլոր տվյալների հոսքը տեղի է ունենում render փուլում։ Սակայն իրական հավելվածները հաճախ ունեն գործողություններ, որոնք կապված են արտաքին աշխարհի հետ՝ օրինակ՝ տվյալների բեռնում API-ից, event listener-ների ավելացում, localStorage-ի հետ աշխատանք, կամ document title-ի փոփոխություն։ Այս բոլոր գործողությունները կոչվում են կողային էֆեկտներ (side effects)։
useEffect-ը hook է, որը թույլ է տալիս գրել կոդ, որը կաշխատի render-ից հետո։
Այն React-ին ասում է․ «երբ այս կոմպոնենտը երևա կամ թարմացվի, արա այս գործողությունը»։
useEffect(() => {
// այստեղ գրիր քո side effect-ը
return () => {
// cleanup (ըստ ցանկության)
};
}, [dependencies]);
Եթե չտաս երկրորդ արգումենտ՝ dependency array, effect-ը կաշխատի ամեն render-ից հետո։
import React, { useState, useEffect } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Component rendered, count =', count);
});
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(prev => prev + 1)}>Ավելացնել</button>
</div>
);
}
Ամեն անգամ, երբ սեղմում ես կոճակը, effect-ը նորից է գործարկվում։ Սա սովորաբար անարդյունավետ է, եթե միայն չես ուզում հետևել state-ի բոլոր փոփոխություններին։
Եթե փոխանցես դատարկ array՝ [], effect-ը կաշխատի միայն մեկ անգամ՝ կոմպոնենտի առաջին mount-ի ժամանակ։
Սա շատ տարածված է՝ API-ից տվյալներ բեռնելու համար։
import React, { useState, useEffect } from 'react';
export default function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
console.log('Fetching data...');
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => setUsers(data));
}, []); // միանգամյա գործարկում
return (
<div>
<h2>Օգտագործողներ</h2>
<ul>
{users.map(u => <li key={u.id}>{u.name}</li>)}
</ul>
</div>
);
}
Կարևոր․ dependency array-ը դատարկ է, նշանակում է՝ effect-ը չի գործարկվի state-ի փոփոխության ժամանակ։
Եթե effect-ը պետք է աշխատի որոշակի state-ի փոփոխության դեպքում՝ նշիր այն dependency array-ում։
import React, { useState, useEffect } from 'react';
export default function Timer() {
const [seconds, setSeconds] = useState(0);
const [running, setRunning] = useState(false);
useEffect(() => {
if (!running) return;
const id = setInterval(() => setSeconds(s => s + 1), 1000);
console.log('Timer started...');
return () => clearInterval(id); // cleanup
}, [running]);
return (
<div>
<p>Ժամանակ՝ {seconds} վայրկյան</p>
<button onClick={() => setRunning(true)}>Start</button>
<button onClick={() => setRunning(false)}>Stop</button>
</div>
);
}
Երբ running դառնում է true՝ setInterval-ը սկսում է գործել։
Երբ դառնում է false՝ cleanup-ը (clearInterval) դադարեցնում է ժամանակաչափը։
Cleanup-ը React-ին օգնում է մաքրել effect-ի թողած հետևանքները, երբ կոմպոնենտը հանվում է DOM-ից կամ dependency-ն փոխվում է։ Օրինակ՝ event listener-ի հեռացում կամ timer-ի չեղարկում։
useEffect(() => {
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
Սխալ մոտեցում՝ event listener ավելացնել առանց cleanup-ի՝ դա կարող է առաջացնել memory leak։
Եթե dependency array-ում մոռանաս ներառել որոշ state, effect-ը կարող է օգտագործել հին արժեքներ (stale values)։
useEffect(() => {
console.log('Current count:', count);
}, []); // ❌ count-ը երբեք չի թարմանա այստեղ
Ճիշտ տարբերակն է՝
useEffect(() => {
console.log('Current count:', count);
}, [count]); // ✅ effect-ը կաշխատի ամեն անգամ, երբ count-ը փոխվի
Ահա պրոֆեսիոնալ մակարդակի օրինակ՝ տվյալների բեռնում, վիճակի կառավարում, և սխալների մշակում։
import React, { useState, useEffect } from 'react';
export default function Posts() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchPosts() {
try {
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!res.ok) throw new Error('Network error');
const data = await res.json();
setPosts(data.slice(0, 5));
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
fetchPosts();
}, []); // միայն mount-ի ժամանակ
if (loading) return <p>Բեռնվում է...</p>;
if (error) return <p>Սխալ՝ {error}</p>;
return (
<div>
<h2>Վերջին գրառումները</h2>
<ul>
{posts.map(p => <li key={p.id}>{p.title}</li>)}
</ul>
</div>
);
}
Սա ցուցադրում է լավագույն պրակտիկան՝ async գործողություններ effect-ի ներսում և loader/error state-երի կառավարում։
Գրիր կոմպոնենտ, որը․