मेरे प्यारे दोस्तों, क्या आपके साथ कभी ऐसा हुआ है कि आपने ReactJS में एक ही function के अंदर लगातार 3 बार setCount(count + 1) लिखा, लेकिन जब button पर click किया, तो state सिर्फ 1 ही बार बढ़ी? या फिर setTimeout या useEffect के अंदर आपको state की पुरानी यानी stale value का सामना करना पड़ा?
घबराइए मत! यह समस्या दुनिया के हर दूसरे React developer के साथ होती है। आज हम इस समस्या की जड़ तक जाएंगे और इसका सबसे बेहतरीन समाधान—React useState Functional Update—को बिल्कुल बारीकी से समझेंगे। इस guide के बाद, आप state updates में कभी कोई गलती नहीं करेंगे। तो चलिए, चाय का कप हाथ में लीजिए और इस शानदार सफर को शुरू करते हैं!
setCount) में direct value पास करने के बजाय एक callback function पास किया जाता है। यह callback automatically current state (previous state) को receive करता है, जिससे stale state bug पूरी तरह से हल हो जाता है।React useState Functional Update क्या है?
जब हम React components में useState hook का उपयोग करते हैं, तो हमें दो चीजें मिलती हैं: current state value और उसे update करने वाला function।
आमतौर पर, हम state को direct update करते हैं, कुछ इस तरह:
const [count, setCount] = useState(0);
// Direct Update
setCount(count + 1);
लेकिन React में state updates तुरंत (synchronously) नहीं होते। React performance को बेहतर बनाने के लिए कई state updates को batch (इकट्ठा) करता है। इस वजह से, जब हम direct update करते हैं, तो React को हमेशा "ताजा" या "current" state value नहीं मिल पाती।
इसी समस्या को हल करने के लिए आता है Functional Update। इसमें हम direct value पास करने के बजाय setCount के अंदर एक arrow function पास करते हैं:
// Functional Update
setCount(prevCount => prevCount + 1);
यहाँ, prevCount वह सबसे ताजा और accurate state value है जो React के पास उस समय मौजूद होती है। React खुद इस callback function को execute करता है और उसमें latest state pass करता है।
हमें Functional Updates की ज़रूरत क्यों पड़ती है? (The Stale State Problem)
इसको समझने के लिए, ध्यान देने वाली बात ये है कि React state updates को कैसे schedule करता है। चलिए एक classic example देखते हैं जिससे यह समस्या पूरी तरह साफ हो जाएगी।
The Counter Bug (Direct Update का फेल होना)
मान लीजिए आपके पास एक function है जहां आप state को consecutive (लगातार) तीन बार बढ़ाना चाहते हैं:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const tripleIncrement = () => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={tripleIncrement}>Triple Increment</button>
</div>
);
}
अगर आप इस button पर click करेंगे, तो आपको लगेगा कि count की value 0 से बढ़कर सीधे 3 हो जानी चाहिए। लेकिन असल में ऐसा नहीं होगा! count सिर्फ 1 से बढ़ेगा।
ऐसा क्यों हुआ? (Under the Hood Story)
जब tripleIncrement function trigger होता है, तब count की value 0 होती है (यह current render का snapshot है)।
- पहला
setCount(count + 1)कहता है:setCount(0 + 1)→ React schedules count to be 1. - दूसरा
setCount(count + 1)भी कहता है:setCount(0 + 1)→ React schedules count to be 1. - तीसरा
setCount(count + 1)भी कहता है:setCount(0 + 1)→ React schedules count to be 1.
चूंकि React batching करता है, वह इन तीनों calls को merge कर देता है और component को केवल एक बार re-render करता है। आखिर में, count की value सिर्फ 1 ही रह जाती है। इसे ही हम Stale State Problem कहते हैं।
Functional Update कैसे काम करता है?
अब, इसी code को functional update के साथ लिख कर देखते हैं:
const tripleIncrement = () => {
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
};
अब जब आप button पर click करेंगे, तो React इन updates को queue (लाइन) में लगा देगा:
- React पहली callback run करेगा:
prevCount(जो 0 है) + 1 = 1. - React दूसरी callback run करेगा: अब queue में latest temporary value 1 है, तो
prevCount(जो 1 है) + 1 = 2. - React तीसरी callback run करेगा: अब latest value 2 है, तो
prevCount(जो 2 है) + 1 = 3.
आखिरकार, जब re-render होगा, तब count की value बिल्कुल सही यानी 3 होगी। यही जादू है Functional Updates का!
Direct Update vs Functional Update में क्या अंतर है?
आइए इन दोनों के बीच के अंतर को एक comparison table के जरिए अच्छे से समझते हैं ताकि कोई doubt न रहे:
| फ़ीचर (Feature) | Direct Update (setCount(count + 1)) | Functional Update (setCount(prev => prev + 1)) |
|---|---|---|
| Syntax | Direct value या variable pass करना। | Callback function pass करना जो previous state return करे। |
| State Accuracy | Stale (पुरानी) value होने का खतरा रहता है। | हमेशा 100% Latest और Fresh state मिलती है। |
| Asynchronous Behavior | setTimeout या Event Handlers में bug पैदा कर सकता है। | Async environments में भी पूरी तरह सुरक्षित और predictable है। |
| Dependencies in useEffect | State variable को dependency array में डालना पड़ता है। | बिना dependency array में state डाले भी सही काम करता है। |
Real-world Examples: Functional Updates का Use कब करें?
चलिए कुछ ऐसे real-world scenarios देखते हैं जहां functional updates का इस्तेमाल करना अनिवार्य हो जाता है। इन examples को आप अपने production apps में भी direct use कर सकते हैं।
Example 1: Delayed Async Counter (setTimeout के साथ)
कई बार हमें state को किसी asynchronous call, API fetch या delay के बाद update करना पड़ता है। यहाँ direct update कैसे fail होता है और functional update उसे कैसे बचाता है, देखिए:
import React, { useState } from 'react';
function AsyncCounter() {
const [count, setCount] = useState(0);
const handleDelayedIncrement = () => {
// 3 second का delay
setTimeout(() => {
// Wrong Way (अगर user 3 seconds में multiple clicks करता है, तो updates miss हो जाएंगे)
// setCount(count + 1);
// Correct Way (functional update हमेशा latest value उठाएगा)
setCount(prevCount => prevCount + 1);
}, 3000);
};
return (
<div style={{ padding: '20px', border: '1px solid #ccc', borderRadius: '8px' }}>
<h3>Async Counter Component</h3>
<p>Count: {count}</p>
<button onClick={handleDelayedIncrement}>Increment After 3s</button>
</div>
);
}
export default AsyncCounter;
Example 2: Complex State Objects और Arrays को Update करना
जब आप MERN Stack application बना रहे होते हैं, तब आपको objects और arrays जैसी complex structures के साथ काम करना पड़ता है। ऐसे में functional updates spread operator (...) के साथ मिलकर बहुत सफाई से काम करते हैं।
यहाँ एक Todo list app का code है जो functional update का उपयोग करके state array में नया item जोड़ता है:
import React, { useState } from 'react';
function TodoApp() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Learn React Basics' },
{ id: 2, text: 'Master useState Hook' }
]);
const [inputText, setInputText] = useState('');
const addTodo = () => {
if (!inputText.trim()) return;
const newTodo = {
id: Date.now(),
text: inputText
};
// Functional update ensuring we do not mutate previous array directly
setTodos(prevTodos => [...prevTodos, newTodo]);
setInputText('');
};
return (
<div style={{ maxWidth: '400px', margin: '20px auto', padding: '10px' }}>
<h2>My Todo App</h2>
<input
type="text"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
placeholder="Enter todo item..."
/>
<button onClick={addTodo}>Add Todo</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
</div>
);
}
export default TodoApp;
Common Errors और उन्हें Debug करने का तरीका
जब आप functional updates का उपयोग करते हैं, तो कुछ ऐसी error आ सकती हैं जो आपको परेशान कर सकती हैं। आइए उनके समाधान पर चर्चा करते हैं।
1. Direct Mutation Error (State को सीधे बदलना)
सबसे बड़ी गलती जो नए developers करते हैं, वो है callback function के अंदर state object को सीधे modify (mutate) कर देना।
// ❌ INCORRECT WAY - Direct mutation inside callback
setUserInfo(prev => {
prev.age = 25; // Directly modifying the object property
return prev; // React may not re-render because object reference didn't change!
});
// CORRECT WAY - Creating a shallow copy
setUserInfo(prev => {
return {
...prev,
age: 25
};
});
Fix: हमेशा state की deep copy या spread operator का उपयोग करके नया object या array return करें ताकि React reference change को detect कर सके और successfully re-render कर सके।
2. Infinite Render Loops inside useEffect
अगर आप state update functions को useEffect में call करते हैं, और direct update का इस्तेमाल करते हैं, तो आपको state को dependency array में डालना पड़ता है। इससे component infinite loop में फंस सकता है।
Fix: Functional updates का उपयोग करें। क्योंकि functional updates को current state variable की direct dependency नहीं चाहिए होती, आप dependency array को खाली रख सकते हैं या dependency array से state variable हटा सकते हैं।
Best Practices: कब क्या इस्तेमाल करें?
सीनियर developer होने के नाते मेरी सलाह यही होगी कि आप इन rules को हमेशा याद रखें:
- Direct Update का उपयोग तब करें: जब नया state value पिछले state value पर बिल्कुल भी निर्भर न हो (जैसे input field की text value update करना,
setSearchQuery(e.target.value))। - Functional Update का उपयोग तब करें: जब भी आपका नया state, पुराने state (previous state) की value पर calculated हो (जैसे toggle switches, counters, array manipulations like adding/deleting items)।
- आप React की official useState API Reference का भी अध्ययन कर सकते हैं ताकि इसके internal behaviors को और गहराई से समझ सकें।
तो दोस्तों, हमने आज सीखा कि React में state stale problems क्यों आती हैं और useState Functional Update हमारी performance और bugs को सुधारने में कितना महत्वपूर्ण रोल अदा करता है। अगली बार जब भी आप state updates करें, इस technique को ज़रूर अपनाएं!


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