
React useReducer vs useState का उपयोग कब और कैसे करें? इस विस्तृत हिन्दी गाइड में स्टेट मैनेजमेंट, कोड उदाहरण और बेस्ट प्रैक्टिसेज को गहराई से सीखें।
जब हम ReactJS में नए-नए प्रोजेक्ट्स बनाना शुरू करते हैं, तो useState हुक हमारा सबसे पहला और पक्का दोस्त बनता है। लेकिन जैसे-जैसे हमारी एप्लीकेशन का साइज बढ़ता है और स्टेट्स आपस में उलझने लगती हैं, हमारे सामने एक बड़ा सवाल आता है—क्या अभी भी हमें useState ही इस्तेमाल करना चाहिए, या अब useReducer का सहारा लेने का समय आ गया है?
⚡ Quick Answer: अगर आपकी स्टेट सरल है (जैसे primitive values या simple objects) तो useState का उपयोग करें। लेकिन जब स्टेट में जटिल लॉजिक, मल्टीपल सब-वैल्यूज, या एक स्टेट का बदलाव दूसरी स्टेट पर निर्भर करता है, तब React useReducer vs useState की जंग में useReducer को चुनना सबसे बेस्ट और स्केलेबल तरीका है।
दोस्तों, इस व्यावहारिक ब्लॉग में हम इन दोनों Hooks के बीच के अंतर को बिल्कुल जीरो से एडवांस लेवल तक समझेंगे, प्रैक्टिकल कोड देखेंगे, और यह भी जानेंगे कि बड़े प्रोजेक्ट्स में परफॉरमेंस को बनाए रखने के लिए कौन सा हुक कब इस्तेमाल करना चाहिए।
---
React में State Management क्या है और यह क्यों जरूरी है?
React में State मूल रूप से एक कंपोनेंट की मेमोरी होती है। यह वह डेटा होल्ड करती है जो यूजर इंटरैक्शन, API रिस्पांस, या किसी अन्य इवेंट के कारण बदल सकता है। जब भी स्टेट बदलती है, React उस React components को फिर से रेंडर (Re-render) करता है ताकि स्क्रीन पर नया डेटा दिखाई दे सके।
जैसे-जैसे आपकी एप्लीकेशन बढ़ती है, स्टेट का स्ट्रक्चर भी जटिल हो जाता है। उदाहरण के लिए, एक सिंपल Counter एप्लीकेशन में केवल एक नंबर को ट्रैक करना होता है, जो कि बहुत आसान है। लेकिन एक Shopping Cart, Multi-step Form, या Dashboard में दर्जनों वेरिएबल्स होते हैं जो एक-दूसरे पर निर्भर करते हैं। यहीं पर सही स्टेट मैनेजमेंट टूल का चुनाव करना महत्वपूर्ण हो जाता है ताकि आपका कोड क्लीन, टेस्टेबल और मेंटेनेबल रहे।
---
useState Hook क्या है और यह कैसे काम करता है?
useState हुक React का सबसे बुनियादी हुक है। यह आपको फंक्शनल कॉम्पोनेंट्स के अंदर लोकल स्टेट जोड़ने की अनुमति देता है। इसका सिंटैक्स बेहद सरल और सीधा है।
ध्यान देने वाली बात ये है कि useState हमें एक एरे (Array) देता है, जिसमें पहली वैल्यू हमारी करंट स्टेट होती है और दूसरी वैल्यू एक सेटर फंक्शन (Setter Function) होती है जो उस स्टेट को अपडेट करती है।
useState का एक लाइव उदाहरण: Signup Form
चलिए एक व्यावहारिक उदाहरण देखते हैं जहाँ हम एक साधारण Signup Form की स्टेट्स को useState का उपयोग करके मैनेज कर रहे हैं:
import React, { useState } from 'react';
export function SimpleSignupForm() {
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [agreeToTerms, setAgreeToTerms] = useState(false);
const handleSubmit = (e) => {
e.preventDefault();
const registrationData = { username, email, password, agreeToTerms };
console.log('Registered User Details:', registrationData);
};
return (
<form onSubmit={handleSubmit} style={{ padding: '20px', maxWidth: '400px', margin: 'auto' }}>
<h2>Register Account</h2>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px' }}>Username:</label>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
style={{ width: '100%', padding: '8px' }}
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px' }}>Email:</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
style={{ width: '100%', padding: '8px' }}
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px' }}>Password:</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
style={{ width: '100%', padding: '8px' }}
/>
</div>
<div style={{ marginBottom: '15px' }}>
<input
type="checkbox"
id="terms"
checked={agreeToTerms}
onChange={(e) => setAgreeToTerms(e.target.checked)}
/>
<label htmlFor="terms" style={{ marginLeft: '8px' }}>I agree to terms and conditions</label>
</div>
<button type="submit" style={{ padding: '10px 15px', background: '#06b6d4', color: '#fff', border: 'none', cursor: 'pointer' }}>
Submit
</button>
</form>
);
}
इस उदाहरण में, हमारे पास 4 अलग-अलग स्टेट्स हैं। यह तब तक तो ठीक है जब तक हमारी फॉर्म रिक्वायरमेंट साधारण हैं। लेकिन सोचिए, अगर हमारे पास 15-20 इनपुट फील्ड्स हों, और उनके वैलिडेशन लॉजिक्स भी एक-दूसरे से जुड़े हों, तो इतने सारे सेटर फंक्शन्स को संभालना हमारे कोड को बहुत गन्दा और अन-ऑर्गनाइज्ड बना देगा।
---
useReducer Hook क्या है और यह जटिल स्टेट को कैसे आसान बनाता है?
useReducer हुक उन डेवलपर्स के लिए एक वरदान है जो Redux जैसे स्टेट मैनेजमेंट लाइब्रेरी से परिचित हैं। यह बड़ी या जटिल स्टेट को संभालने के लिए एक अधिक संरचित (Structured) तरीका प्रदान करता है।
जब एक स्टेट में बदलाव दूसरे स्टेट पर निर्भर करता है, या जब बहुत सारे एक्शन्स (जैसे ADD, REMOVE, UPDATE) एक सिंगल स्टेट ऑब्जेक्ट पर होते हैं, तो useReducer का उपयोग करना सबसे बेहतरीन विकल्प होता है।
useReducer कैसे काम करता है?
useReducer मुख्य रूप से तीन चीजों पर काम करता है:
- Reducer Function: एक शुद्ध फंक्शन (Pure Function) जो वर्तमान स्टेट (Current State) और एक्शन (Action) को लेता है और एक नई स्टेट रिटर्न करता है।
- Initial State: वह शुरुआती स्टेट जो हम कॉम्पोनेंट लोड होने पर सेट करते हैं।
- Dispatch Method: एक ट्रिगर फंक्शन जिसके द्वारा हम Reducer को कोई भी 'Action' भेजते हैं।
🏗️ useReducer State Flow Diagram
UI / User Action
→
Dispatch(action)
→
Reducer (Pure Function)
→
New State & Re-render
Diagram: flow representation of dispatching actions to transform state in React applications.
useReducer का एक लाइव उदाहरण: Ecommerce Shopping Cart
चलिए एक रियल-वर्ल्ड शॉपिंग कार्ट का उदाहरण देखते हैं जहाँ आइटम्स को जोड़ना, हटाना, मात्रा (Quantity) बढ़ाना और कार्ट खाली करना जैसी कई स्टेट ट्रांजिशन्स शामिल हैं:
import React, { useReducer } from 'react';
const initialState = {
cartItems: [],
totalPrice: 0
};
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_TO_CART': {
const existingIndex = state.cartItems.findIndex(item => item.id === action.payload.id);
let updatedCartItems;
if (existingIndex >= 0) {
updatedCartItems = state.cartItems.map((item, idx) =>
idx === existingIndex ? { ...item, quantity: item.quantity + 1 } : item
);
} else {
updatedCartItems = [...state.cartItems, { ...action.payload, quantity: 1 }];
}
const newTotal = updatedCartItems.reduce((acc, curr) => acc + (curr.price * curr.quantity), 0);
return { ...state, cartItems: updatedCartItems, totalPrice: newTotal };
}
case 'REMOVE_FROM_CART': {
const updatedCartItems = state.cartItems.filter(item => item.id !== action.payload);
const newTotal = updatedCartItems.reduce((acc, curr) => acc + (curr.price * curr.quantity), 0);
return { ...state, cartItems: updatedCartItems, totalPrice: newTotal };
}
case 'UPDATE_QUANTITY': {
const updatedCartItems = state.cartItems.map(item => {
if (item.id === action.payload.id) {
return { ...item, quantity: Math.max(1, action.payload.quantity) };
}
return item;
});
const newTotal = updatedCartItems.reduce((acc, curr) => acc + (curr.price * curr.quantity), 0);
return { ...state, cartItems: updatedCartItems, totalPrice: newTotal };
}
case 'CLEAR_CART':
return { cartItems: [], totalPrice: 0 };
default:
throw new Error(`Unhandled action type: ${action.type}`);
}
}
export function ShoppingCartComponent() {
const [state, dispatch] = useReducer(cartReducer, initialState);
const productsList = [
{ id: 'p1', name: 'Developer Laptop', price: 1200 },
{ id: 'p2', name: 'Mechanical Keyboard', price: 150 },
{ id: 'p3', name: 'Ergonomic Mouse', price: 80 }
];
return (
<div style={{ padding: '24px', maxWidth: '600px', margin: 'auto', fontFamily: 'sans-serif' }}>
<h2>🛒 Professional Shopping Cart</h2>
<h3>Products Available:</h3>
<div style={{ display: 'flex', gap: '10px', marginBottom: '20px' }}>
{productsList.map(prod => (
<div key={prod.id} style={{ border: '1px solid #ccc', padding: '10px', borderRadius: '6px' }}>
<p style={{ margin: '0 0 8px 0' }}><strong>{prod.name}</strong> - ${prod.price}</p>
<button
onClick={() => dispatch({ type: 'ADD_TO_CART', payload: prod })}
style={{ background: '#2563eb', color: '#fff', border: 'none', padding: '6px 10px', borderRadius: '4px', cursor: 'pointer' }}
>
Add to Cart
</button>
</div>
))}
</div>
<h3>Your Cart Summary:</h3>
{state.cartItems.length === 0 ? (
<p>Your cart is currently empty.</p>
) : (
<div>
<ul style={{ paddingLeft: '20px' }}>
{state.cartItems.map(item => (
<li key={item.id} style={{ marginBottom: '10px' }}>
{item.name} (Qty: {item.quantity}) - ${item.price * item.quantity}
<button
onClick={() => dispatch({ type: 'UPDATE_QUANTITY', payload: { id: item.id, quantity: item.quantity + 1 } })}
style={{ margin: '0 5px', padding: '2px 6px' }}
>
+
</button>
<button
onClick={() => dispatch({ type: 'UPDATE_QUANTITY', payload: { id: item.id, quantity: item.quantity - 1 } })}
style={{ margin: '0 5px', padding: '2px 6px' }}
>
-
</button>
<button
onClick={() => dispatch({ type: 'REMOVE_FROM_CART', payload: item.id })}
style={{ marginLeft: '10px', background: '#dc2626', color: '#fff', border: 'none', padding: '3px 8px', borderRadius: '4px', cursor: 'pointer' }}
>
Remove
</button>
</li>
))}
</ul>
<h4>Total Price: ${state.totalPrice}</h4>
<button
onClick={() => dispatch({ type: 'CLEAR_CART' })}
style={{ background: '#4b5563', color: '#fff', border: 'none', padding: '8px 12px', borderRadius: '4px', cursor: 'pointer' }}
>
Clear Cart
</button>
</div>
)}
</div>
);
}
इस कोड को ध्यान से देखिए। सारे कार्ट ऑपरेशन्स (Add, Remove, Update, Clear) कॉम्पोनेंट के UI कोड से पूरी तरह अलग एक सिंगल cartReducer फंक्शन के अंदर लिखे गए हैं। यह पृथक्करण (Separation of Concerns) कोड को क्लीन, रीडेबल और टेस्ट करने में बहुत आसान बनाता है।
---
React useReducer vs useState में मुख्य अंतर क्या हैं?
चाहें तो आप हर जगह useReducer इस्तेमाल कर सकते हैं, या फिर केवल useState से भी काम चला सकते हैं, लेकिन स्मार्ट डेवलपर सही काम के लिए सही टूल चुनते हैं। नीचे दी गई टेबल आपको दोनों के बीच मुख्य अंतर को समझने में मदद करेगी:
| फीचर / पैरामीटर | useState Hook | useReducer Hook |
|---|---|---|
| स्टेट का प्रकार | साधारण (Primitive, Simple Object, Strings) | जटिल और नेस्टेड (Complex Objects, Nested Arrays) |
| स्टेट अपडेट लॉजिक | कॉम्पोनेंट के अंदर (Inline with UI code) | कॉम्पोनेंट के बाहर (Centralized Reducer Function) |
| रीडेबिलिटी और मेंटेनेंस | कम स्टेट्स के लिए बेहतरीन, ज्यादा होने पर बिखरा हुआ | बड़े और जटिल स्टेट लॉजिक्स के लिए बहुत साफ-सुथरा |
| टेस्टेबिलिटी (Testing) | अलग से टेस्ट करना मुश्किल है (UI के साथ टेस्ट होता है) | बहुत आसान है, क्योंकि Reducer एक Pure Function है |
| लर्निंग कर्व (Learning Curve) | बहुत आसान और क्विक | मध्यम (Reducer, Action, Payload समझना होता है) |
---
React useReducer vs useState का use कब करें? (Practical Rules)
क्या आपको अभी भी निर्णय लेने में भ्रम है? कोई बात नहीं! आप नीचे दिए गए सरल नियमों को अपने प्रोजेक्ट्स में लागू कर सकते हैं:
useState का उपयोग करें जब:
- आपकी स्टेट पूरी तरह से स्वतंत्र है (जैसे एक इनपुट फील्ड, सिर्फ एक बुलेटिन / toggle, या एक ओपन/क्लोज साइडबार)।
- स्टेट केवल स्ट्रिंग्स, नंबर्स, या बुलियन्स जैसी सरल वैल्यूज को होल्ड करती है।
- स्टेट अपडेट करने का लॉजिक कॉम्पोनेंट के अंदर 1 या 2 लाइन्स का ही है।
- आपको यूनिट टेस्टिंग लिखने की कोई विशेष आवश्यकता नहीं है।
useReducer का उपयोग करें जब:
- स्टेट ट्रांजिशन्स का फ्लो जटिल है और एक बदलाव से दूसरे वैल्यू पर असर पड़ता है (जैसे ई-कॉमर्स कार्ट, जटिल फ़िल्टर सिस्टम)।
- आपकी स्टेट में बहुत सारी सब-वैल्यूज (Nested Object) शामिल हैं।
- आप स्टेट अपडेट लॉजिक को कॉम्पोनेंट के रेंडरिंग लॉजिक से पूरी तरह से अलग और क्लीन रखना चाहते हैं।
- आपको स्टेट म्यूटेशन्स के लिए गहरे यूनिट टेस्ट्स लिखने की आवश्यकता है।
- आप React Context API का उपयोग करके ग्लोबल स्टेट मैनेज कर रहे हैं (useContext + useReducer एक मिनी-रेडक्स की तरह काम करता है)।
अगर आप React के बेस्ट प्रैक्टिसेज के बारे में अधिक विस्तार से जानना चाहते हैं, तो आप Official React Documentation पर जा सकते हैं जो हमेशा सबसे विश्वसनीय गाइडलाइन प्रदान करता है।
---
Common Errors और Debugging: डेवलपर्स कहाँ गलती करते हैं?
दोस्तों, चलिए अब उन गलतियों पर बात करते हैं जो डेवलपर्स अक्सर करते हैं और फिर घंटों बग्स को फिक्स करने में बिता देते हैं।
1. Reducer में State को सीधे Mutate करना
यह सबसे आम error है। React का रेंडरिंग सिस्टम उथली तुलना (Shallow Comparison) पर निर्भर करता है। अगर आप स्टेट ऑब्जेक्ट को डायरेक्ट बदल देते हैं, तो React को पता ही नहीं चलेगा कि स्टेट बदली है और स्क्रीन री-रेंडर नहीं होगी।
// ❌ गलत तरीका (Do NOT do this!)
function badReducer(state, action) {
if (action.type === 'ADD_ITEM') {
state.cartItems.push(action.payload); // Direct mutation! React won't re-render!
return state;
}
}
// सही तरीका (Always do this!)
function goodReducer(state, action) {
if (action.type === 'ADD_ITEM') {
return {
...state,
cartItems: [...state.cartItems, action.payload] // Immutable update
};
}
}
2. Action Types का गलत नाम (Typo Errors)
Reducer में स्ट्रिंग्स के साथ सीधे तुलना करते समय स्पेलिंग की गलतियां होना आम है। जैसे आपने dispatch करते समय 'ADD_TO_CART' लिखा और reducer में 'ADD_TO_CARD' से चेक किया। इसे आसानी से fix error करने के लिए हमेशा Action Types के लिए CONSTANTS का उपयोग करें:
// Define action types as constants
const ACTIONS = {
ADD: 'ADD_TO_CART',
REMOVE: 'REMOVE_FROM_CART',
CLEAR: 'CLEAR_CART'
};
// dispatch inside component
dispatch({ type: ACTIONS.ADD, payload: product });
---
Performance and Scalability Best Practices
बड़े प्रोजेक्ट्स में परफॉरमेंस को बेहतर बनाए रखने के लिए इन टिप्स को ध्यान में रखें:
- Avoid Inline Functions in Dispatch: कॉम्पोनेंट के JSX में सीधे बड़े कस्टमाइज्ड फंक्शन्स लिखने के बजाय, एक्शन्स को क्लीन रूप से हैंडलर्स में रैप करें।
-
Keep Reducer Pure: Reducer में कभी भी API कॉल्स, लोकलस्टोरेज रीड/राइट, या
Date.now()जैसे साइड-इफेक्ट्स न डालें। वे बिल्कुल शुद्ध (Pure) होने चाहिए। हमेशा साइड इफेक्ट्स को कॉम्पोनेंट केuseEffectमें संभालें और फिर डेटा को dispatch करें। -
Memoize Callbacks: अगर आप सेटर फंक्शन्स या dispatch को चाइल्ड कॉम्पोनेंट्स में पास कर रहे हैं, तो ध्यान रखें कि
dispatchफ़ंक्शन हमेशा स्थिर (Stable) रहता है, इसलिए इसेuseCallbackके डिपेंडेंसी एरे में पास करने की आवश्यकता नहीं होती।
---
तो दोस्तों, हमने आज सीखा कि कैसे React useReducer vs useState के बीच का चुनाव आपकी कोडिंग लाइफ को बेहद आसान और व्यवस्थित बना सकता है। जब बात छोटे कॉम्पोनेंट्स की हो, तो useState के साथ जाएं और बिना किसी संकोच के तुरंत कोड लिखना शुरू करें। लेकिन जब चीजें जटिल होने लगें और कॉम्पोनेंट भारी होने लगे, तो बिना संकोच किए useReducer को अपनाएं। यह आपके कोड को भविष्य के लिए स्केलेबल और सुंदर रखेगा। हैप्पी कोडिंग!
---
Frequently Asked Questions (FAQs)
Q1: क्या useReducer हुक useState से ज्यादा फास्ट है?
नहीं, परफॉरमेंस के मामले में दोनों में कोई बहुत बड़ा अंतर नहीं है। useReducer भी परदे के पीछे useState के समान री-रेंडरिंग मेकैनिज्म का उपयोग करता है। यह केवल आपके कोड को अधिक ऑर्गनाइज्ड और पठनीय (Readable) बनाता है।
Q2: क्या मैं एक ही कॉम्पोनेंट में useState और useReducer दोनों का उपयोग कर सकता हूँ?
हाँ, बिल्कुल! आप दोनों को एक साथ इस्तेमाल कर सकते हैं। उदाहरण के लिए, एक शॉपिंग कार्ट की मुख्य स्टेट को आप useReducer से और उस कार्ट के केवल एक छोटे से UI पॉपअप की ओपन/क्लोज स्टेट को useState से मैनेज कर सकते हैं।
Q3: क्या useReducer के आने के बाद Redux का उपयोग बंद हो गया है?
नहीं, Redux एक बहुत बड़ा और शक्तिशाली ग्लोबल स्टेट मैनेजमेंट टूल है जिसमें डेव-टूल्स (Redux DevTools), मिडलवेयर (Middleware), और एक्स्ट्रा स्टेट लॉजिक्स होते हैं। useReducer मुख्य रूप से लोकल कॉम्पोनेंट लेवल या Context API के साथ मिलकर सीमित ग्लोबल लेवल स्टेट को मैनेज करने के लिए होता है।
Q4: Reducer फ़ंक्शन में "Pure Function" होने का क्या मतलब है?
Pure Function का मतलब है कि अगर आप समान इनपुट (State & Action) देंगे, तो वह हमेशा बिल्कुल समान आउटपुट (New State) देगा। इसमें कोई साइड इफेक्ट जैसे API कॉल्स, रैंडम नंबर जनरेशन, या कॉम्पोनेंट के बाहर के वेरिएबल्स को बदलना नहीं होना चाहिए।
Q5: क्या useReducer का उपयोग केवल बड़े फॉर्म्स में ही करना चाहिए?
नहीं, आप किसी भी ऐसे कॉम्पोनेंट में इसका उपयोग कर सकते हैं जहाँ स्टेट अपडेट करने के कई अलग-अलग तरीके या एक्शन्स (जैसे dynamic lists, complex filters, dashboards, dynamic workflows) शामिल होते हैं।
Top comments (0)