क्या आप भी अपने React components में बहुत सारे useState Hooks लिखकर परेशान हो चुके हैं? जब हमारी एप्लीकेशन बड़ी होने लगती है, तो स्टेट आपस में उलझने लगती है और कोड को संभालना एक बहुत बड़ा सिरदर्द बन जाता है। मेरे 50 साल के प्रोग्रामिंग करियर में मैंने देखा है कि डेवलपर्स अक्सर स्टेट मैनेजमेंट को शुरुआत में नजरअंदाज करते हैं और बाद में उनका कोड एक उलझा हुआ मकड़जाल बन जाता है।
आज इस गाइड में, हम और आप मिलकर React useReducer Hook का बिल्कुल पोस्टमार्टम करेंगे। हम समझेंगे कि यह हुक क्यों बनाया गया, यह अंदरूनी तौर पर कैसे काम करता है, और कब आपको useState को छोड़कर useReducer का रास्ता चुनना चाहिए। तो अपनी चाय/कॉफी बगल में रख लीजिए, और चलिए एक साथ इस सफर को शुरू करते हैं!
---React useReducer Hook क्या है और यह कैसे काम करता है?
सरल शब्दों में कहें तो, useReducer एक ऐसा हुक है जो हमें React में complex state transition को संभालने की ताकत देता है। अगर आपने कभी Redux पर काम किया है, तो आपको यह कांसेप्ट बहुत ही जाना-पहचाना लगेगा। वास्तव में, useReducer भी उसी Redux-like flow (Action -> Dispatch -> Reducer -> State) पर काम करता है।
जब हमारा स्टेट लॉजिक बहुत जटिल हो जाता है (जैसे कि एक ऑब्जेक्ट जिसमें कई नेस्टेड प्रॉपर्टीज हों), तो useState से उसे मैनेज करना मुश्किल हो जाता है। ऐसे में useReducer हमारे काम को बेहद आसान बना देता है।
useReducer के चार मुख्य पिलर (Core Concepts):
- State: यह आपकी एप्लीकेशन का करंट डेटा या वैल्यू है।
- Action: यह एक साधारण JavaScript Object होता है जो यह बताता है कि हम स्टेट के साथ क्या करना चाहते हैं। इसमें आमतौर पर एक
typeप्रॉपर्टी होती है। - Reducer Function: यह एक pure function होता है जो current state और action को इनपुट के रूप में लेता है, और एक नई स्टेट रिटर्न करता है।
- Dispatch: यह एक फंक्शन है जिसे हम कॉल करते हैं जब भी हमें कोई एक्शन ट्रिगर करना होता है। यह एक्शन को Reducer Function तक पहुँचाने का काम करता है।
ध्यान देने वाली बात ये है कि Reducer Function को हमेशा एक Pure Function होना चाहिए। इसका मतलब है कि यह कोई side effects (जैसे API calls, LocalStorage updates, या random numbers generate करना) नहीं कर सकता। इसे केवल इनपुट लेकर एक नया स्टेट प्रेडिक्टेबल तरीके से रिटर्न करना होता है। इसके बारे में अधिक जानने के लिए आप Official React useReducer Documentation भी देख सकते हैं।
---useState vs useReducer: दोनों में क्या अंतर है?
अक्सर डेवलपर्स मुझसे पूछते हैं, "सर, जब हमारे पास पहले से ही useState है, तो हमें useReducer की क्या जरूरत है?"
यह एक बहुत ही जायज सवाल है। इसे समझने के लिए नीचे दी गई तुलनात्मक तालिका (Comparison Table) को ध्यान से देखिए:
| फ़ीचर / Feature | useState Hook | useReducer Hook |
|---|---|---|
| State Type | सरल और स्वतंत्र स्टेट (Primitive Types, Simple Objects) | जटिल और परस्पर निर्भर स्टेट (Nested Objects, Arrays) |
| State Logic Location | Component के अंदर ही परिभाषित होता है | Component के बाहर Reducer function में अलग किया जा सकता है |
| Readability & Maintenance | कम स्टेट्स के लिए बहुत आसान है, लेकिन बढ़ने पर कोड बिखर जाता है | शुरुआत में थोड़ा सेटअप लगता है, लेकिन बाद में कोड बहुत साफ़ रहता है |
| Testing | Unit testing करना थोड़ा कठिन होता है | Reducer pure function होने के कारण testing बेहद आसान होती है |
तो दोस्तों, नियम बहुत सरल है: यदि आपकी स्टेट केवल एक स्ट्रिंग, नंबर, या साधारण बुलियन (boolean) है, तो आँख बंद करके useState का उपयोग करें। लेकिन यदि आपकी स्टेट का अगला रूप इस बात पर निर्भर करता है कि उसका पिछला रूप क्या था, या फिर कई स्टेट्स एक साथ अपडेट हो रही हैं, तो तुरंत useReducer को काम पर लगा दीजिए।
React useReducer Hook का syntax क्या है?
चलिए, सबसे पहले इसके बेसिक सिंटैक्स को देख लेते हैं ताकि जब हम कोड लिखें, तो हमारे दिमाग में कोई भी कन्फ्यूजन न रहे।
const [state, dispatch] = useReducer(reducer, initialArg, init);
यहाँ पर ये तीन पैरामीटर्स क्या हैं, इसे आसान भाषा में समझें:
- reducer: वह फ़ंक्शन जो यह तय करेगा कि स्टेट कैसे बदलेगी। यह वर्तमान स्टेट और एक्शन को लेता है।
- initialArg: वह शुरुआती मान (Initial State) जो आप अपनी स्टेट को देना चाहते हैं।
- init (Optional): एक ऐसा फ़ंक्शन जिसका उपयोग इनिशियल स्टेट को आलसी तरीके से (Lazily) कैलकुलेट करने के लिए किया जाता है।
Step-by-Step Implementation: Shopping Cart State को useReducer से कैसे Manage करें?
अब समय आ गया है असली कोडिंग करने का! हम एक रियल-वर्ल्ड प्रोडक्शन-रेडी Shopping Cart Application का उदाहरण लेंगे। इस एप्लीकेशन में यूजर कार्ट में आइटम जोड़ सकता है, आइटम की क्वांटिटी बढ़ा या घटा सकता है, आइटम को हटा सकता है और पूरी कार्ट को एक क्लिक में खाली कर सकता है।
यह काम useState से करना एक नाइटमेयर (दुःस्वप्न) साबित हो सकता है, लेकिन देखिए हम इसे useReducer के साथ कितनी खूबसूरती से करते हैं।
Step 1: Initial State और Action Types को Define करना
सबसे पहले, हम अपनी इनिशियल स्टेट का आकार तय करेंगे और यह तय करेंगे कि हमारी कार्ट क्या-क्या एक्शन ले सकती है।
import React, { useReducer } from 'react';
// 1. Initial State डिफाइन करें
const initialState = {
cart: [],
totalItems: 0,
totalPrice: 0,
};
// 2. Action Types को कांस्टेंट के रूप में रखें ताकि स्पेलिंग मिस्टेक्स न हों
const ACTIONS = {
ADD_ITEM: 'ADD_ITEM',
REMOVE_ITEM: 'REMOVE_ITEM',
UPDATE_QUANTITY: 'UPDATE_QUANTITY',
CLEAR_CART: 'CLEAR_CART',
};
Step 2: Reducer Function लिखना (The Core Brain)
अब हम अपना जादुई Reducer फ़ंक्शन लिखेंगे। ध्यान रहे कि हमें स्टेट को सीधे म्यूटेट (Mutate) नहीं करना है। हमें हमेशा एक नया ऑब्जेक्ट रिटर्न करना है (Immutability Principle)।
function cartReducer(state, action) {
switch (action.type) {
case ACTIONS.ADD_ITEM: {
const existingItemIndex = state.cart.findIndex(item => item.id === action.payload.id);
let updatedCart;
if (existingItemIndex >= 0) {
// अगर आइटम पहले से कार्ट में है, तो उसकी क्वांटिटी बढ़ाएं
updatedCart = state.cart.map((item, index) =>
index === existingItemIndex
? { ...item, quantity: item.quantity + 1 }
: item
);
} else {
// नया आइटम कार्ट में जोड़ें
updatedCart = [...state.cart, { ...action.payload, quantity: 1 }];
}
// टोटल कैलकुलेट करें
const totalItems = updatedCart.reduce((sum, item) => sum + item.quantity, 0);
const totalPrice = updatedCart.reduce((sum, item) => sum + item.price * item.quantity, 0);
return {
...state,
cart: updatedCart,
totalItems,
totalPrice,
};
}
case ACTIONS.REMOVE_ITEM: {
const updatedCart = state.cart.filter(item => item.id !== action.payload);
const totalItems = updatedCart.reduce((sum, item) => sum + item.quantity, 0);
const totalPrice = updatedCart.reduce((sum, item) => sum + item.price * item.quantity, 0);
return {
...state,
cart: updatedCart,
totalItems,
totalPrice,
};
}
case ACTIONS.UPDATE_QUANTITY: {
const updatedCart = state.cart.map(item => {
if (item.id === action.payload.id) {
return { ...item, quantity: action.payload.quantity };
}
return item;
}).filter(item => item.quantity > 0); // अगर क्वांटिटी 0 हो जाए तो हटा दें
const totalItems = updatedCart.reduce((sum, item) => sum + item.quantity, 0);
const totalPrice = updatedCart.reduce((sum, item) => sum + item.price * item.quantity, 0);
return {
...state,
cart: updatedCart,
totalItems,
totalPrice,
};
}
case ACTIONS.CLEAR_CART:
return initialState;
default:
throw new Error(`Unhandled action type: ${action.type}`);
}
}
Step 3: Component में useReducer को Integrate करना
अब हम ऊपर बनाए गए Reducer और State का इस्तेमाल अपने JSX में करेंगे।
export default function ShoppingCartApp() {
const [state, dispatch] = useReducer(cartReducer, initialState);
// डेमो प्रोडक्ट्स की लिस्ट
const products = [
{ id: 1, name: 'Apple iPhone 15 Pro', price: 120000 },
{ id: 2, name: 'MacBook Air M2', price: 95000 },
{ id: 3, name: 'Sony WH-1000XM5 Headphones', price: 28000 },
];
return (
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif', maxWidth: '800px', margin: '0 auto' }}>
<h1 style={{ color: '#1e293b' }}>🛒 My Shopping Cart</h1>
{/* प्रोडक्ट्स की लिस्ट */}
<div style={{ marginBottom: '30px' }}>
<h2>Available Products</h2>
<div style={{ display: 'flex', gap: '15px' }}>
{products.map(product => (
<div key={product.id} style={{ border: '1px solid #cbd5e1', padding: '15px', borderRadius: '8px', flex: 1 }}>
<h3 style={{ margin: '0 0 10px 0' }}>{product.name}</h3>
<p>Price: ₹{product.price.toLocaleString()}</p>
<button
onClick={() => dispatch({ type: ACTIONS.ADD_ITEM, payload: product })}
style={{ background: '#0284c7', color: '#fff', border: 'none', padding: '8px 12px', borderRadius: '4px', cursor: 'pointer' }}
>
Add to Cart
</button>
</div>
))}
</div>
</div>
{/* कार्ट सेक्शन */}
<div style={{ borderTop: '2px solid #e2e8f0', paddingTop: '20px' }}>
<h2>Your Cart ({state.totalItems} Items)</h2>
{state.cart.length === 0 ? (
<p style={{ color: '#64748b' }}>Your cart is empty. Start shopping!</p>
) : (
<div>
<ul style={{ listStyleType: 'none', padding: 0 }}>
{state.cart.map(item => (
<li key={item.id} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px', background: '#f8fafc', padding: '10px', borderRadius: '6px' }}>
<div>
<strong>{item.name}</strong> - ₹{item.price.toLocaleString()} x {item.quantity}
</div>
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
<button
onClick={() => dispatch({ type: ACTIONS.UPDATE_QUANTITY, payload: { id: item.id, quantity: item.quantity - 1 } })}
style={{ padding: '4px 8px' }}
>
-
</button>
<span>{item.quantity}</span>
<button
onClick={() => dispatch({ type: ACTIONS.UPDATE_QUANTITY, payload: { id: item.id, quantity: item.quantity + 1 } })}
style={{ padding: '4px 8px' }}
>
+
</button>
<button
onClick={() => dispatch({ type: ACTIONS.REMOVE_ITEM, payload: item.id })}
style={{ background: '#ef4444', color: '#fff', border: 'none', padding: '5px 10px', borderRadius: '4px', cursor: 'pointer' }}
>
Remove
</button>
</div>
</li>
))}
</ul>
<div style={{ marginTop: '20px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<h3>Total Price: ₹{state.totalPrice.toLocaleString()}</h3>
<button
onClick={() => dispatch({ type: ACTIONS.CLEAR_CART })}
style={{ background: '#475569', color: '#fff', border: 'none', padding: '10px 16px', borderRadius: '6px', cursor: 'pointer' }}
>
Clear Cart
</button>
</div>
</div>
)}
</div>
</div>
);
}
इस कोड को आप अपने ReactJS प्रोजेक्ट में सीधे चलाकर देख सकते हैं। यह पूरी तरह से फंक्शनल और एरर-फ्री है। कोड की सरलता और उसका आर्किटेक्चर देखिए - सारा स्टेट लॉजिक कॉम्पोनेंट से बाहर cartReducer में है, जिससे हमारा UI कॉम्पोनेंट कितना साफ़ और पढ़ने योग्य बन गया है!
React useReducer Hook में आने वाले Common Errors और Solutions क्या हैं?
डेवलपर्स अक्सर useReducer का उपयोग करते समय कुछ ऐसी गलतियाँ कर बैठते हैं जिससे एप्लीकेशन में अजीबोगरीब बग्स आ जाते हैं। चलिए, उन्हें समय रहते पहचान लेते हैं ताकि आप उन समस्याओं को आसानी से fix error कर सकें।
1. State Mutation (स्टेट को डायरेक्ट बदलना)
सबसे कॉमन error यह है कि डेवलपर्स सीधे स्टेट ऑब्जेक्ट को म्यूटेट कर देते हैं:
// ❌ गलत तरीका (This will not trigger re-render)
case ACTIONS.CLEAR_CART:
state.cart = [];
return state;
💡 Solution: React ऑब्जेक्ट के रेफरेंस चेंज होने पर ही रेंडर करता है। इसलिए हमेशा एक नया ऑब्जेक्ट रिटर्न करें, जैसे:
// ✅ सही तरीका (Always return a new reference)
case ACTIONS.CLEAR_CART:
return {
...state,
cart: []
};
2. Default Case भूल जाना
अगर आपने रिड्यूसर फ़ंक्शन के अंत में default केस नहीं लिखा है, और कोई ऐसा एक्शन डिस्पैच हो गया जो वहां डिफाइन नहीं था, तो आपकी स्टेट undefined हो जाएगी।
💡 Solution: हमेशा अंत में एक डिफॉल्ट केस रखें जो या तो करंट स्टेट रिटर्न करे या फिर एक एरर थ्रो करे ताकि आपको डेवलपमेंट के दौरान तुरंत पता चल सके।
default:
throw new Error(`No action matches type: ${action.type}`);
---
Best Practices for Performance and Scalability
जब आप एक बहुत बड़ी एंटरप्राइज-लेवल एप्लीकेशन बना रहे हों, तो इन बातों का ध्यान जरूर रखें:
- React Context API के साथ संयोजन: बड़े प्रोजेक्ट्स में Prop Drilling से बचने के लिए
useReducerकोuseContextके साथ जोड़कर इस्तेमाल करें। इससे आपको बिना Redux इंस्टॉल किए एक ग्लोबल स्टेट मैनेजमेंट सिस्टम मिल जाता है। - Dispatch Function की स्थिरता: React गारंटी देता है कि
dispatchफ़ंक्शन का रेफरेंस कभी नहीं बदलता। इसलिए आपको इसेuseEffectयाuseCallbackके डिपेंडेंसी ऐरे में डालने की कोई जरूरत नहीं है। - Lazy Initialization: अगर आपकी इनिशियल स्टेट को कैलकुलेट करने में भारी सीपीयू प्रोसेसिंग लगती है, तो तीसरे पैरामीटर (init function) का उपयोग करें ताकि वह केवल पहले रेंडर पर ही चले।
तो दोस्तों, हमने आज विस्तार से सीखा कि React useReducer Hook क्या है, यह कैसे काम करता है, और इसे एक रियल-वर्ल्ड शॉपिंग कार्ट के जरिए कैसे लागू किया जाता है। उम्मीद है अब आपका यह डर दूर हो गया होगा और आप अपने आने वाले प्रोजेक्ट्स में इसका खुलकर उपयोग करेंगे। कोडिंग करते रहिए, नया सीखते रहिए, क्योंकि प्रोग्रामिंग में ठहराव ही अंत है!
---

टिप्पणियाँ
एक टिप्पणी भेजें