मजबूत साइड इफ़ेक्ट मैनेजमेंट के लिए React के useEffect हुक की पूरी क्षमता को अनलॉक करें। यह गाइड वैश्विक डेवलपर्स के लिए मूलभूत अवधारणाओं, सामान्य पैटर्न, उन्नत तकनीकों और सर्वोत्तम प्रथाओं को कवर करता है।
React useEffect में महारत हासिल करें: साइड इफ़ेक्ट मैनेजमेंट पैटर्न के लिए एक व्यापक गाइड
आधुनिक वेब डेवलपमेंट की गतिशील दुनिया में, React यूजर इंटरफेस बनाने के लिए एक शक्तिशाली लाइब्रेरी के रूप में सामने आता है। इसका कंपोनेंट-आधारित आर्किटेक्चर डिक्लेरेटिव प्रोग्रामिंग को प्रोत्साहित करता है, जिससे UI बनाना सहज और कुशल हो जाता है। हालाँकि, एप्लिकेशन शायद ही कभी अकेले मौजूद होते हैं; उन्हें अक्सर बाहरी दुनिया के साथ इंटरैक्ट करने की आवश्यकता होती है – डेटा प्राप्त करना, सब्सक्रिप्शन सेट अप करना, DOM में हेरफेर करना, या तीसरे पक्ष की लाइब्रेरी के साथ एकीकृत करना। इन इंटरैक्शन्स को "साइड इफेक्ट्स" के रूप में जाना जाता है।
यहीं पर useEffect हुक आता है, जो React में फंक्शनल कंपोनेंट्स का एक आधारशिला है। React Hooks के साथ पेश किया गया, useEffect इन साइड इफेक्ट्स को मैनेज करने का एक शक्तिशाली और सुरुचिपूर्ण तरीका प्रदान करता है, जो पहले क्लास कंपोनेंट लाइफसाइकिल मेथड्स (जैसे componentDidMount, componentDidUpdate, और componentWillUnmount) में पाई जाने वाली क्षमताओं को सीधे फंक्शनल कंपोनेंट्स में लाता है। useEffect को समझना और उसमें महारत हासिल करना सिर्फ क्लीन कोड लिखने के बारे में नहीं है; यह अधिक परफॉर्मेंट, विश्वसनीय और मेंटेनेबल React एप्लिकेशन बनाने के बारे में है।
यह व्यापक गाइड आपको useEffect की गहराई में ले जाएगा, इसके मूलभूत सिद्धांतों, सामान्य उपयोग के मामलों, उन्नत पैटर्न और महत्वपूर्ण सर्वोत्तम प्रथाओं की खोज करेगा। चाहे आप एक अनुभवी React डेवलपर हों जो अपनी समझ को मजबूत करना चाहते हैं या हुक्स में नए हैं और इस आवश्यक अवधारणा को समझने के लिए उत्सुक हैं, आपको यहाँ बहुमूल्य जानकारी मिलेगी। हम बेसिक डेटा फेचिंग से लेकर जटिल डिपेंडेंसी मैनेजमेंट तक सब कुछ कवर करेंगे, यह सुनिश्चित करते हुए कि आप किसी भी साइड इफेक्ट परिदृश्य को संभालने के लिए सुसज्जित हैं।
1. useEffect की मूलभूत बातें समझना
इसके मूल में, useEffect आपको फंक्शनल कंपोनेंट्स में साइड इफेक्ट्स करने की अनुमति देता है। यह अनिवार्य रूप से React को बताता है कि आपके कंपोनेंट को रेंडर के बाद कुछ करने की आवश्यकता है। React तब आपके "इफेक्ट" फंक्शन को चलाएगा जब वह DOM में बदलावों को फ्लश कर चुका होगा।
React में साइड इफेक्ट्स क्या हैं?
साइड इफेक्ट्स वे ऑपरेशन होते हैं जो बाहरी दुनिया को प्रभावित करते हैं या किसी बाहरी सिस्टम के साथ इंटरैक्ट करते हैं। React के संदर्भ में, इसका अक्सर मतलब होता है:
- डेटा फेचिंग: डेटा प्राप्त करने या भेजने के लिए API कॉल करना।
- सब्सक्रिप्शन: इवेंट श्रोताओं (जैसे, यूजर इनपुट, ग्लोबल इवेंट्स के लिए), WebSocket कनेक्शन, या रीयल-टाइम डेटा स्ट्रीम सेट अप करना।
- DOM मैनिपुलेशन: ब्राउज़र के डॉक्यूमेंट ऑब्जेक्ट मॉडल (DOM) के साथ सीधे इंटरैक्ट करना (जैसे, डॉक्यूमेंट का टाइटल बदलना, फोकस मैनेज करना, नॉन-React लाइब्रेरी के साथ एकीकृत करना)।
- टाइमर:
setTimeoutयाsetIntervalका उपयोग करना। - लॉगिंग: एनालिटिक्स डेटा भेजना।
बेसिक useEffect सिंटैक्स
useEffect हुक दो आर्गुमेंट्स लेता है:
- एक फंक्शन जिसमें साइड इफेक्ट लॉजिक होता है। यह फंक्शन वैकल्पिक रूप से एक क्लीनअप फंक्शन लौटा सकता है।
- एक वैकल्पिक डिपेंडेंसी ऐरे।
import React, { useEffect, useState } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
// This is the side effect function
console.log('Component rendered or count changed:', count);
// Optional cleanup function
return () => {
console.log('Cleanup for count:', count);
};
}, [count]); // Dependency array
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
डिपेंडेंसी ऐरे: नियंत्रण की कुंजी
useEffect का दूसरा आर्गुमेंट, डिपेंडेंसी ऐरे, यह नियंत्रित करने के लिए महत्वपूर्ण है कि इफेक्ट कब चलता है। React इफेक्ट को केवल तभी फिर से चलाएगा जब डिपेंडेंसी ऐरे में कोई भी मान रेंडर के बीच बदल गया हो।
-
कोई डिपेंडेंसी ऐरे नहीं: इफेक्ट कंपोनेंट के हर रेंडर के बाद चलता है। यह प्रदर्शन-महत्वपूर्ण इफेक्ट्स जैसे डेटा फेचिंग के लिए शायद ही कभी चाहा जाता है, क्योंकि यह अनंत लूप या अनावश्यक री-एग्जीक्यूशन का कारण बन सकता है।
useEffect(() => { // Runs after every render }); -
खाली डिपेंडेंसी ऐरे (
[]): इफेक्ट केवल प्रारंभिक रेंडर (माउंट) के बाद एक बार चलता है और क्लीनअप फंक्शन केवल कंपोनेंट के अनमाउंट होने से पहले एक बार चलता है। यह उन इफेक्ट्स के लिए आदर्श है जो केवल एक बार होने चाहिए, जैसे प्रारंभिक डेटा फेचिंग या ग्लोबल इवेंट श्रोताओं को सेट करना।useEffect(() => { // Runs once on mount console.log('Component mounted!'); return () => { // Runs once on unmount console.log('Component unmounted!'); }; }, []); -
मानों के साथ डिपेंडेंसी ऐरे (
[propA, stateB]): इफेक्ट प्रारंभिक रेंडर के बाद और जब भी ऐरे में कोई मान बदलता है तब चलता है। यह सबसे आम और बहुमुखी उपयोग का मामला है, जो यह सुनिश्चित करता है कि आपका इफेक्ट लॉजिक प्रासंगिक डेटा परिवर्तनों के साथ सिंक्रनाइज़ है।useEffect(() => { // Runs on mount and whenever 'userId' changes fetchUser(userId); }, [userId]);
क्लीनअप फंक्शन: लीक और बग्स को रोकना
कई साइड इफेक्ट्स को एक "क्लीनअप" चरण की आवश्यकता होती है। उदाहरण के लिए, यदि आप एक सब्सक्रिप्शन सेट करते हैं, तो मेमोरी लीक को रोकने के लिए कंपोनेंट के अनमाउंट होने पर आपको अनसब्सक्राइब करने की आवश्यकता होती है। यदि आप एक टाइमर शुरू करते हैं, तो आपको इसे क्लियर करने की आवश्यकता है। क्लीनअप फंक्शन आपके useEffect कॉलबैक से लौटाया जाता है।
React क्लीनअप फंक्शन को इफेक्ट को फिर से चलाने से पहले (यदि डिपेंडेंसी बदलती है) और कंपोनेंट के अनमाउंट होने से पहले चलाता है। यह सुनिश्चित करता है कि संसाधन ठीक से जारी किए गए हैं और रेस कंडीशंस या स्टेल क्लोजर्स जैसी संभावित समस्याओं को कम किया गया है।
useEffect(() => {
const subscription = subscribeToChat(props.chatId);
return () => {
// Cleanup: Unsubscribe when chatId changes or component unmounts
unsubscribeFromChat(subscription);
};
}, [props.chatId]);
2. सामान्य useEffect उपयोग के मामले और पैटर्न
आइए उन व्यावहारिक परिदृश्यों का पता लगाएं जहां useEffect चमकता है, साथ ही प्रत्येक के लिए सर्वोत्तम प्रथाओं के साथ।
2.1. डेटा फेचिंग
डेटा फेचिंग शायद useEffect का सबसे आम उपयोग का मामला है। आप डेटा तब फेच करना चाहते हैं जब कंपोनेंट माउंट होता है या जब विशिष्ट प्रॉप्स/स्टेट मान बदलते हैं।
माउंट पर बेसिक फेच
import React, { useEffect, useState } from 'react';
function UserProfile() {
const [userData, setUserData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUserData = async () => {
try {
const response = await fetch('https://api.example.com/users/1');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUserData(data);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchUserData();
}, []); // Empty array ensures this runs only once on mount
if (loading) return <p>Loading user data...</p>;
if (error) return <p>Error: {error.message}</p>;
if (!userData) return <p>No user data found.</p>;
return (
<div>
<h2>{userData.name}</h2>
<p>Email: {userData.email}</p>
<p>Location: {userData.location}</p>
</div>
);
}
डिपेंडेंसी के साथ फेचिंग
अक्सर, जो डेटा आप फेच करते हैं वह कुछ डायनेमिक मान पर निर्भर करता है, जैसे कि यूजर आईडी, सर्च क्वेरी, या पेज नंबर। जब ये डिपेंडेंसी बदलती हैं, तो आप डेटा को फिर से फेच करना चाहते हैं।
import React, { useEffect, useState } from 'react';
function UserPosts({ userId }) {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!userId) { // Handle cases where userId might be undefined initially
setPosts([]);
setLoading(false);
return;
}
const fetchUserPosts = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(`https://api.example.com/users/${userId}/posts`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setPosts(data);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchUserPosts();
}, [userId]); // Re-fetch whenever userId changes
if (loading) return <p>Loading posts...</p>;
if (error) return <p>Error: {error.message}</p>;
if (posts.length === 0) return <p>No posts found for this user.</p>;
return (
<div>
<h3>Posts by User {userId}</h3>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
डेटा फेचिंग के साथ रेस कंडीशंस को संभालना
जब डिपेंडेंसी तेजी से बदलती हैं, तो आप रेस कंडीशंस का सामना कर सकते हैं जहां एक पुराना, धीमा नेटवर्क अनुरोध एक नए, तेज अनुरोध के बाद पूरा होता है, जिससे पुराना डेटा प्रदर्शित होता है। इसे कम करने का एक सामान्य पैटर्न एक फ्लैग या AbortController का उपयोग करना है।
import React, { useEffect, useState } from 'react';
function ProductDetails({ productId }) {
const [product, setProduct] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
const fetchProduct = async () => {
setLoading(true);
setError(null);
setProduct(null); // Clear previous product data
try {
const response = await fetch(`https://api.example.com/products/${productId}`, { signal });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setProduct(data);
} catch (err) {
if (err.name === 'AbortError') {
console.log('Fetch aborted');
} else {
setError(err);
}
} finally {
setLoading(false);
}
};
fetchProduct();
return () => {
// Abort ongoing fetch request if component unmounts or productId changes
controller.abort();
};
}, [productId]);
if (loading) return <p>Loading product details...</p>;
if (error) return <p>Error: {error.message}</p>;
if (!product) return <p>No product found.</p>;
return (
<div>
<h2>{product.name}</h2>
<p>Price: ${product.price}</p>
<p>Description: {product.description}</p>
</div>
);
}
2.2. इवेंट श्रोता और सब्सक्रिप्शन
इवेंट श्रोताओं (जैसे, कीबोर्ड इवेंट्स, विंडो रीसाइज) या बाहरी सब्सक्रिप्शन (जैसे, WebSockets, चैट सेवाएं) का प्रबंधन करना एक क्लासिक साइड इफेक्ट है। मेमोरी लीक को रोकने और यह सुनिश्चित करने के लिए कि इवेंट हैंडलर अब आवश्यक नहीं होने पर हटा दिए जाते हैं, क्लीनअप फंक्शन यहां महत्वपूर्ण है।
ग्लोबल इवेंट श्रोता
import React, { useEffect, useState } from 'react';
function WindowSizeLogger() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
const handleResize = () => {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
};
window.addEventListener('resize', handleResize);
return () => {
// Clean up the event listener when component unmounts
window.removeEventListener('resize', handleResize);
};
}, []); // Empty array: add/remove listener only once on mount/unmount
return (
<div>
<p>Window Width: {windowSize.width}px</p>
<p>Window Height: {windowSize.height}px</p>
</div>
);
}
चैट सेवा सब्सक्रिप्शन
import React, { useEffect, useState } from 'react';
// Assume chatService is an external module providing subscribe/unsubscribe methods
import { chatService } from './chatService';
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const handleNewMessage = (message) => {
setMessages((prevMessages) => [...prevMessages, message]);
};
const subscription = chatService.subscribe(roomId, handleNewMessage);
return () => {
chatService.unsubscribe(subscription);
};
}, [roomId]); // Re-subscribe if roomId changes
return (
<div>
<h3>Chat Room: {roomId}</h3>
<div className="messages">
{messages.length === 0 ? (
<p>No messages yet.</p>
) : (
messages.map((msg, index) => (
<p key={index}><strong>{msg.sender}:</strong> {msg.text}</p>
))
)}
</div>
</div>
);
}
2.3. DOM मैनिपुलेशन
जबकि React की डिक्लेरेटिव प्रकृति अक्सर सीधे DOM मैनिपुलेशन को सारगर्भित करती है, ऐसे समय होते हैं जब आपको रॉ DOM के साथ इंटरैक्ट करने की आवश्यकता होती है, खासकर जब तीसरे पक्ष की लाइब्रेरी के साथ एकीकृत करते हैं जो सीधे DOM एक्सेस की उम्मीद करते हैं।
डॉक्यूमेंट टाइटल को संशोधित करना
import React, { useEffect } from 'react';
function PageTitleUpdater({ title }) {
useEffect(() => {
document.title = `My App | ${title}`;
}, [title]); // Update title whenever 'title' prop changes
return (
<h2>Welcome to the {title} Page!</h2>
);
}
एक तीसरे पक्ष की चार्ट लाइब्रेरी (जैसे, Chart.js) के साथ एकीकृत करना
import React, { useEffect, useRef } from 'react';
import Chart from 'chart.js/auto'; // Assuming Chart.js is installed
function MyChartComponent({ data, labels }) {
const chartRef = useRef(null); // Ref to hold the canvas element
const chartInstance = useRef(null); // Ref to hold the chart instance
useEffect(() => {
if (chartRef.current) {
// Destroy existing chart instance before creating a new one
if (chartInstance.current) {
chartInstance.current.destroy();
}
const ctx = chartRef.current.getContext('2d');
chartInstance.current = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Sales Data',
data: data,
backgroundColor: 'rgba(75, 192, 192, 0.6)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
}
});
}
return () => {
// Cleanup: Destroy the chart instance on unmount
if (chartInstance.current) {
chartInstance.current.destroy();
}
};
}, [data, labels]); // Re-render chart if data or labels change
return (
<div style={{ width: '600px', height: '400px' }}>
<canvas ref={chartRef}></canvas>
</div>
);
}
2.4. टाइमर
React कंपोनेंट्स के भीतर setTimeout या setInterval का उपयोग करने के लिए सावधानीपूर्वक प्रबंधन की आवश्यकता होती है ताकि टाइमर को कंपोनेंट के अनमाउंट होने के बाद चलने से रोका जा सके, जिससे त्रुटियां या मेमोरी लीक हो सकती हैं।
सिंपल काउंटडाउन टाइमर
import React, { useEffect, useState } from 'react';
function CountdownTimer({ initialSeconds }) {
const [seconds, setSeconds] = useState(initialSeconds);
useEffect(() => {
if (seconds <= 0) return; // Stop timer when it reaches zero
const timerId = setInterval(() => {
setSeconds(prevSeconds => prevSeconds - 1);
}, 1000);
return () => {
// Cleanup: Clear the interval when component unmounts or seconds become 0
clearInterval(timerId);
};
}, [seconds]); // Re-run effect if seconds changes to set up new interval (e.g. if initialSeconds changes)
return (
<div>
<h3>Countdown: {seconds} seconds</h3>
{seconds === 0 && <p>Time's up!</p>}
</div>
);
}
3. उन्नत useEffect पैटर्न और नुकसान
जबकि useEffect की मूल बातें सीधी हैं, इसमें महारत हासिल करने में अधिक सूक्ष्म व्यवहारों और सामान्य नुकसानों को समझना शामिल है।
3.1. स्टेल क्लोजर्स और पुराने मान
useEffect (और सामान्य रूप से जावास्क्रिप्ट क्लोजर्स) के साथ एक आम समस्या पिछले रेंडर से "स्टेल" मानों तक पहुंचना है। यदि आपका इफेक्ट क्लोजर एक स्टेट या प्रॉप को कैप्चर करता है जो बदलता है, लेकिन आप इसे डिपेंडेंसी ऐरे में शामिल नहीं करते हैं, तो इफेक्ट पुराने मान को देखता रहेगा।
इस समस्याग्रस्त उदाहरण पर विचार करें:
import React, { useEffect, useState } from 'react';
function StaleClosureExample() {
const [count, setCount] = useState(0);
useEffect(() => {
// This effect wants to log the count after 2 seconds.
// If count changes within these 2 seconds, this will log the OLD count!
const timer = setTimeout(() => {
console.log('Stale Count:', count);
}, 2000);
return () => {
clearTimeout(timer);
};
}, []); // Problem: 'count' is not in dependencies, so it's stale
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
इसे ठीक करने के लिए, सुनिश्चित करें कि आपके इफेक्ट के अंदर उपयोग किए गए सभी मान जो प्रॉप्स या स्टेट से आते हैं, डिपेंडेंसी ऐरे में शामिल हैं:
import React, { useEffect, useState } from 'react';
function FixedClosureExample() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
console.log('Correct Count:', count);
}, 2000);
return () => {
clearTimeout(timer);
};
}, [count]); // Solution: 'count' is now a dependency. Effect re-runs when count changes.
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
हालांकि, डिपेंडेंसी जोड़ने से कभी-कभी इफेक्ट बहुत बार चल सकता है। यह हमें अन्य पैटर्न पर लाता है:
स्टेट के लिए फंक्शनल अपडेट्स का उपयोग करना
जब पिछले मान के आधार पर स्टेट को अपडेट करते हैं, तो set- फंक्शन के फंक्शनल अपडेट फॉर्म का उपयोग करें। यह डिपेंडेंसी ऐरे में स्टेट वेरिएबल को शामिल करने की आवश्यकता को समाप्त करता है।
import React, { useEffect, useState } from 'react';
function AutoIncrementer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(prevCount => prevCount + 1); // Functional update
}, 1000);
return () => clearInterval(interval);
}, []); // 'count' is not a dependency because we use functional update
return <p>Count: {count}</p>;
}
useRef उन परिवर्तनीय मानों के लिए जो री-रेंडर का कारण नहीं बनते
कभी-कभी आपको एक परिवर्तनीय मान संग्रहीत करने की आवश्यकता होती है जो री-रेंडर को ट्रिगर नहीं करता है, लेकिन आपके इफेक्ट के अंदर सुलभ है। useRef इसके लिए एकदम सही है।
import React, { useEffect, useRef, useState } from 'react';
function LatestValueLogger() {
const [count, setCount] = useState(0);
const latestCountRef = useRef(count); // Create a ref
// Keep the ref's current value updated with the latest count
useEffect(() => {
latestCountRef.current = count;
}, [count]);
useEffect(() => {
const interval = setInterval(() => {
// Access the latest count via the ref, avoiding stale closure
console.log('Latest Count:', latestCountRef.current);
}, 2000);
return () => clearInterval(interval);
}, []); // Empty dependency array, as we are not directly using 'count' here
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
useCallback और useMemo स्थिर डिपेंडेंसी के लिए
जब कोई फंक्शन या ऑब्जेक्ट आपके useEffect की डिपेंडेंसी होता है, तो यह इफेक्ट को अनावश्यक रूप से फिर से चलाने का कारण बन सकता है यदि फंक्शन/ऑब्जेक्ट का संदर्भ हर रेंडर पर बदलता है (जो आमतौर पर होता है)। useCallback और useMemo इन मानों को मेमोइज़ करके, एक स्थिर संदर्भ प्रदान करके मदद करते हैं।
समस्याग्रस्त उदाहरण:
import React, { useEffect, useState } from 'react';
function UserSettings() {
const [userId, setUserId] = useState(1);
const [settings, setSettings] = useState({});
const fetchSettings = async () => {
// This function is re-created on every render
console.log('Fetching settings for user:', userId);
const response = await fetch(`https://api.example.com/users/${userId}/settings`);
const data = await response.json();
setSettings(data);
};
useEffect(() => {
fetchSettings();
}, [fetchSettings]); // Problem: fetchSettings changes on every render
return (
<div>
<p>User ID: {userId}</p>
<button onClick={() => setUserId(userId + 1)}>Next User</button>
<pre>{JSON.stringify(settings, null, 2)}</pre>
</div>
);
}
useCallback के साथ समाधान:
import React, { useEffect, useState, useCallback } from 'react';
function UserSettingsOptimized() {
const [userId, setUserId] = useState(1);
const [settings, setSettings] = useState({});
const fetchSettings = useCallback(async () => {
console.log('Fetching settings for user:', userId);
const response = await fetch(`https://api.example.com/users/${userId}/settings`);
const data = await response.json();
setSettings(data);
}, [userId]); // fetchSettings only changes when userId changes
useEffect(() => {
fetchSettings();
}, [fetchSettings]); // Now fetchSettings is a stable dependency
return (
<div>
<p>User ID: {userId}</p>
<button onClick={() => setUserId(userId + 1)}>Next User</button>
<pre>{JSON.stringify(settings, null, 2)}</pre>
</div>
);
}
इसी तरह, ऑब्जेक्ट्स या ऐरे के लिए, एक स्थिर संदर्भ बनाने के लिए useMemo का उपयोग करें:
import React, { useEffect, useMemo, useState } from 'react';
function ProductList({ categoryId, sortBy }) {
const [products, setProducts] = useState([]);
// Memoize the filter/sort criteria object
const fetchCriteria = useMemo(() => ({
category: categoryId,
sort: sortBy,
}), [categoryId, sortBy]);
useEffect(() => {
// fetch products based on fetchCriteria
console.log('Fetching products with criteria:', fetchCriteria);
// ... API call logic ...
}, [fetchCriteria]); // Effect runs only when categoryId or sortBy changes
return (
<div>
<h3>Products in Category {categoryId} (Sorted by {sortBy})</h3>
<!-- Render product list -->
</div>
);
}
3.2. अनंत लूप्स
एक अनंत लूप तब हो सकता है जब एक इफेक्ट एक स्टेट वेरिएबल को अपडेट करता है जो उसकी डिपेंडेंसी ऐरे में भी है, और अपडेट हमेशा एक री-रेंडर का कारण बनता है जो इफेक्ट को फिर से ट्रिगर करता है। यह डिपेंडेंसी के साथ सावधान न रहने पर एक आम नुकसान है।
import React, { useEffect, useState } from 'react';
function InfiniteLoopExample() {
const [data, setData] = useState([]);
useEffect(() => {
// This will cause an infinite loop!
// setData causes a re-render, which re-runs the effect, which calls setData again.
setData([1, 2, 3]);
}, [data]); // 'data' is a dependency, and we're always setting a new array reference
return <p>Data length: {data.length}</p>;
}
इसे ठीक करने के लिए, सुनिश्चित करें कि आपका इफेक्ट केवल तभी चलता है जब वास्तव में आवश्यक हो या फंक्शनल अपडेट्स का उपयोग करें। यदि आप केवल माउंट पर एक बार डेटा सेट करना चाहते हैं, तो एक खाली डिपेंडेंसी ऐरे का उपयोग करें।
import React, { useEffect, useState } from 'react';
function CorrectDataSetup() {
const [data, setData] = useState([]);
useEffect(() => {
// This runs only once on mount
setData([1, 2, 3]);
}, []); // Empty array prevents re-runs
return <p>Data length: {data.length}</p>;
}
3.3. useEffect के साथ परफॉर्मेंस ऑप्टिमाइजेशन
चिंताओं को कई useEffect हुक्स में विभाजित करना
सभी साइड इफेक्ट्स को एक बड़े useEffect में डालने के बजाय, उन्हें कई हुक्स में विभाजित करें। प्रत्येक useEffect तब अपनी डिपेंडेंसी और क्लीनअप लॉजिक का प्रबंधन कर सकता है। यह कोड को अधिक पठनीय, मेंटेनेबल बनाता है, और अक्सर असंबंधित इफेक्ट्स के अनावश्यक री-रन को रोकता है।
import React, { useEffect, useState } from 'react';
function UserDashboard({ userId }) {
const [profile, setProfile] = useState(null);
const [activityLog, setActivityLog] = useState([]);
// Effect for fetching user profile (depends only on userId)
useEffect(() => {
const fetchProfile = async () => {
// ... fetch profile data ...
console.log('Fetching profile for', userId);
const response = await fetch(`https://api.example.com/users/${userId}/profile`);
const data = await response.json();
setProfile(data);
};
fetchProfile();
}, [userId]);
// Effect for fetching activity log (also depends on userId, but separate concern)
useEffect(() => {
const fetchActivity = async () => {
// ... fetch activity data ...
console.log('Fetching activity for', userId);
const response = await fetch(`https://api.example.com/users/${userId}/activity`);
const data = await response.json();
setActivityLog(data);
};
fetchActivity();
}, [userId]);
return (
<div>
<h2>User Dashboard: {userId}</h2>
<h3>Profile:</h3>
<pre>{JSON.stringify(profile, null, 2)}</pre>
<h3>Activity Log:</h3>
<pre>{JSON.stringify(activityLog, null, 2)}</pre>
</div>
);
}
3.4. पुन: प्रयोज्यता के लिए कस्टम हुक्स
जब आप खुद को कई कंपोनेंट्स में एक ही useEffect लॉजिक लिखते हुए पाते हैं, तो यह एक मजबूत संकेतक है कि आप इसे एक कस्टम हुक में सारगर्भित कर सकते हैं। कस्टम हुक्स वे फंक्शन होते हैं जो use से शुरू होते हैं और अन्य हुक्स को कॉल कर सकते हैं, जिससे आपका लॉजिक पुन: प्रयोज्य और परीक्षण में आसान हो जाता है।
उदाहरण: useFetch कस्टम हुक
import React, { useEffect, useState } from 'react';
// Custom Hook: useFetch.js
function useFetch(url, dependencies = []) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const abortController = new AbortController();
const signal = abortController.signal;
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(url, { signal });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setData(result);
} catch (err) {
if (err.name === 'AbortError') {
console.log('Fetch aborted');
} else {
setError(err);
}
} finally {
setLoading(false);
}
};
fetchData();
return () => {
abortController.abort();
};
}, [url, ...dependencies]); // Re-run if URL or any extra dependency changes
return { data, loading, error };
}
// Component using the custom hook: UserDataDisplay.js
function UserDataDisplay({ userId }) {
const { data: userData, loading, error } = useFetch(
`https://api.example.com/users/${userId}`,
[userId] // Pass userId as a dependency to the custom hook
);
if (loading) return <p>Loading user data...</p>;
if (error) return <p>Error: {error.message}</p>;
if (!userData) return <p>No user data.</p>;
return (
<div>
<h2>{userData.name}</h2>
<p>Email: {userData.email}</p>
</div>
);
}
4. useEffect का उपयोग कब *नहीं* करें
शक्तिशाली होने के बावजूद, useEffect हमेशा हर काम के लिए सही उपकरण नहीं होता है। इसका दुरुपयोग अनावश्यक जटिलता, परफॉर्मेंस संबंधी समस्याओं, या मुश्किल से डीबग होने वाले लॉजिक का कारण बन सकता है।
4.1. व्युत्पन्न स्टेट या संगणित मानों के लिए
यदि आपके पास ऐसी स्टेट है जिसे सीधे अन्य मौजूदा स्टेट या प्रॉप्स से संगणित किया जा सकता है, तो आपको useEffect की आवश्यकता नहीं है। इसे रेंडर के दौरान सीधे गणना करें।
खराब अभ्यास:
function ProductCalculator({ price, quantity }) {
const [total, setTotal] = useState(0);
useEffect(() => {
setTotal(price * quantity); // Unnecessary effect
}, [price, quantity]);
return <p>Total: ${total.toFixed(2)}</p>;
}
अच्छा अभ्यास:
function ProductCalculator({ price, quantity }) {
const total = price * quantity; // Computed directly
return <p>Total: ${total.toFixed(2)}</p>;
}
यदि गणना महंगी है, तो useMemo पर विचार करें, लेकिन फिर भी useEffect नहीं।
import React, { useMemo } from 'react';
function ComplexProductCalculator({ items }) {
const memoizedTotal = useMemo(() => {
console.log('Recalculating total...');
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}, [items]);
return <p>Complex Total: ${memoizedTotal.toFixed(2)}</p>;
}
4.2. प्रॉप या स्टेट परिवर्तनों के लिए जो चाइल्ड कंपोनेंट्स के री-रेंडर को ट्रिगर करना चाहिए
बच्चों को डेटा पास करने और उनके री-रेंडर को ट्रिगर करने का प्राथमिक तरीका प्रॉप्स के माध्यम से है। पैरेंट कंपोनेंट में useEffect का उपयोग उस स्टेट को अपडेट करने के लिए न करें जो फिर एक प्रॉप के रूप में पास हो जाता है, जब एक सीधा प्रॉप अपडेट पर्याप्त होगा।
4.3. उन इफेक्ट्स के लिए जिन्हें क्लीनअप की आवश्यकता नहीं है और जो विशुद्ध रूप से विज़ुअल हैं
यदि आपका साइड इफेक्ट विशुद्ध रूप से विज़ुअल है और इसमें कोई बाहरी सिस्टम, सब्सक्रिप्शन, या टाइमर शामिल नहीं है, और इसे क्लीनअप की आवश्यकता नहीं है, तो आपको useEffect की आवश्यकता नहीं हो सकती है। सरल विज़ुअल अपडेट्स या एनिमेशन के लिए जो बाहरी स्टेट पर निर्भर नहीं करते हैं, CSS या सीधे React कंपोनेंट रेंडरिंग पर्याप्त हो सकती है।
निष्कर्ष: मजबूत अनुप्रयोगों के लिए useEffect में महारत हासिल करना
useEffect हुक मजबूत और रिएक्टिव React एप्लिकेशन बनाने का एक अनिवार्य हिस्सा है। यह React के डिक्लेरेटिव UI और साइड इफेक्ट्स की अनिवार्य प्रकृति के बीच के अंतर को सुरुचिपूर्ण ढंग से पाटता है। इसके मूलभूत सिद्धांतों को समझकर - इफेक्ट फंक्शन, डिपेंडेंसी ऐरे, और महत्वपूर्ण क्लीनअप मैकेनिज्म - आप इस पर बारीक नियंत्रण प्राप्त करते हैं कि आपके साइड इफेक्ट्स कब और कैसे निष्पादित होते हैं।
हमने सामान्य डेटा फेचिंग और इवेंट मैनेजमेंट से लेकर रेस कंडीशंस और स्टेल क्लोजर्स जैसी जटिल परिदृश्यों को संभालने तक, पैटर्न की एक विस्तृत श्रृंखला का पता लगाया है। हमने इफेक्ट लॉजिक को सारगर्भित करने और पुन: उपयोग करने में कस्टम हुक्स की शक्ति पर भी प्रकाश डाला है, एक अभ्यास जो विविध परियोजनाओं और वैश्विक टीमों में कोड मेंटेनेबिलिटी और पठनीयता को महत्वपूर्ण रूप से बढ़ाता है।
useEffect में महारत हासिल करने के लिए इन प्रमुख बातों को याद रखें:
- सच्चे साइड इफेक्ट्स को पहचानें: "बाहरी दुनिया" (APIs, DOM, सब्सक्रिप्शन, टाइमर) के साथ इंटरैक्शन के लिए
useEffectका उपयोग करें। - डिपेंडेंसी को सावधानीपूर्वक प्रबंधित करें: डिपेंडेंसी ऐरे आपका प्राथमिक नियंत्रण है। स्पष्ट रहें कि आपका इफेक्ट किन मानों पर निर्भर करता है ताकि स्टेल क्लोजर्स और अनावश्यक री-रन को रोका जा सके।
- क्लीनअप को प्राथमिकता दें: हमेशा विचार करें कि क्या आपके इफेक्ट को क्लीनअप की आवश्यकता है (जैसे, अनसब्सक्राइब करना, टाइमर क्लियर करना, अनुरोधों को निरस्त करना) ताकि मेमोरी लीक को रोका जा सके और एप्लिकेशन की स्थिरता सुनिश्चित हो सके।
- चिंताओं को अलग करें: एक ही कंपोनेंट के भीतर अलग, असंबंधित साइड इफेक्ट्स के लिए कई
useEffectहुक्स का उपयोग करें। - कस्टम हुक्स का लाभ उठाएं: मॉड्यूलरिटी और पुन: प्रयोज्यता में सुधार के लिए जटिल या पुन: प्रयोज्य
useEffectलॉजिक को कस्टम हुक्स में एनकैप्सुलेट करें। - सामान्य नुकसान से बचें: अनंत लूप्स से सावधान रहें और सुनिश्चित करें कि आप सरल व्युत्पन्न स्टेट या सीधे प्रॉप पासिंग के लिए
useEffectका उपयोग नहीं कर रहे हैं।
इन पैटर्न और सर्वोत्तम प्रथाओं को लागू करके, आप अपने React अनुप्रयोगों में आत्मविश्वास के साथ साइड इफेक्ट्स का प्रबंधन करने के लिए अच्छी तरह से सुसज्जित होंगे, दुनिया भर के उपयोगकर्ताओं के लिए उच्च-गुणवत्ता, परफॉर्मेंट और स्केलेबल यूजर एक्सपीरियंस का निर्माण करेंगे। प्रयोग करते रहें, सीखते रहें, और React के साथ अद्भुत चीजें बनाते रहें!