📚 Advanced Hooks Kya Hain?
Advanced Hooks React ke powerful features hain jo complex state management, DOM manipulation, aur reusable logic create karne mein help karte hain. Basic hooks (useState, useEffect) ke baad, yeh hooks aapko production-level applications banane ki capability dete hain.
Jab aapka application bada ho jata hai, tab simple useState kaafi nahi hota. Tab aapko useReducer chahiye complex state logic ke liye, useRef chahiye DOM elements ko directly access karne ke liye, aur custom hooks chahiye reusable logic banane ke liye.
Jab aapka application bada ho jata hai, tab simple useState kaafi nahi hota. Tab aapko useReducer chahiye complex state logic ke liye, useRef chahiye DOM elements ko directly access karne ke liye, aur custom hooks chahiye reusable logic banane ke liye.
🎯 Kyun Seekhna Zaroori Hai?
- Complex State Management: Multiple related states ko efficiently manage karo
- Performance Optimization: Unnecessary re-renders avoid karo
- DOM Access: Direct DOM manipulation jab zaroori ho
- Reusable Logic: Custom hooks se code duplication reduce karo
- Production Ready: Real-world applications mein use hone wale patterns
useState
→
useReducer
→
useRef
→
Custom Hooks
💡 Learning Path: Pehle basic hooks (useState, useEffect) master karo, phir advanced hooks seekho. Har hook ka apna specific use case hai - sab jagah har hook use karne ki zaroorat nahi hai!
1️⃣ useReducer - Complex State Management
useReducer ek hook hai jo complex state logic ko manage karne ke liye use hota hai. Jab aapke paas multiple sub-values hain ya next state previous state pe depend karta hai, tab useReducer useState se better hai. Yeh Redux pattern follow karta hai - actions dispatch karo aur reducer function state update karta hai.
// Basic useReducer Syntax
const [state, dispatch] = useReducer(reducer, initialState);
// Reducer Function - Pure function jo state aur action leta hai
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
case 'RESET':
return { count: 0 };
default:
return state;
}
}
// Component mein use
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
<button onClick={() => dispatch({ type: 'RESET' })}>Reset</button>
</div>
);
}
// Complex Example - Form State Management
const initialFormState = {
username: '',
email: '',
password: '',
errors: {},
isSubmitting: false
};
function formReducer(state, action) {
switch (action.type) {
case 'UPDATE_FIELD':
return {
...state,
[action.field]: action.value,
errors: { ...state.errors, [action.field]: null }
};
case 'SET_ERRORS':
return { ...state, errors: action.errors };
case 'SUBMIT_START':
return { ...state, isSubmitting: true };
case 'SUBMIT_SUCCESS':
return initialFormState;
case 'SUBMIT_ERROR':
return { ...state, isSubmitting: false, errors: action.errors };
default:
return state;
}
}
🎮 Interactive Demo: useReducer Counter
0
Current State:
{ count: 0 }
{ count: 0 }
📋 Action Log:
❌ useState - Complex State
const [count, setCount] = useState(0);
const [step, setStep] = useState(1);
const [history, setHistory] = useState([]);
// Multiple setState calls
const increment = () => {
setCount(count + step);
setHistory([...history, count + step]);
};
Multiple related states, hard to maintain!
✅ useReducer - Clean & Organized
const [state, dispatch] = useReducer(
reducer,
{ count: 0, step: 1, history: [] }
);
// Single dispatch
const increment = () => {
dispatch({ type: 'INCREMENT' });
};
Single source of truth, predictable updates!
⚠️ useReducer vs useState - Kab Kaunsa Use Karein:
useState use karo jab:
• Simple, independent state values
• State updates simple hain
• Ek ya do state variables
useReducer use karo jab:
• Complex state logic with multiple sub-values
• Next state depends on previous state
• State transitions need to be predictable
• Multiple related state updates
• Testing aur debugging easy banana hai
useState use karo jab:
• Simple, independent state values
• State updates simple hain
• Ek ya do state variables
useReducer use karo jab:
• Complex state logic with multiple sub-values
• Next state depends on previous state
• State transitions need to be predictable
• Multiple related state updates
• Testing aur debugging easy banana hai
2️⃣ useRef - Mutable Values & DOM Access
useRef ek hook hai jo do main purposes ke liye use hota hai:
1. DOM Elements ko Access karna: Direct DOM manipulation ke liye (focus, scroll, animations)
2. Mutable Values Store karna: Values jo re-render trigger nahi karti (timers, previous values, etc.)
useRef ek object return karta hai jiska
1. DOM Elements ko Access karna: Direct DOM manipulation ke liye (focus, scroll, animations)
2. Mutable Values Store karna: Values jo re-render trigger nahi karti (timers, previous values, etc.)
useRef ek object return karta hai jiska
.current property mutable hai. Yeh value change hone pe component re-render nahi hota, jo useState se different hai.
// Basic useRef Syntax
const refContainer = useRef(initialValue);
// DOM Reference Example
function TextInputWithFocusButton() {
const inputRef = useRef(null);
const focusInput = () => {
// Direct DOM access
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus Input</button>
</>
);
}
// Mutable Value Example - Previous State
function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
});
const prevCount = prevCountRef.current;
return (
<div>
<p>Current: {count}, Previous: {prevCount}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
// Timer Example - Storing Interval ID
function Timer() {
const [seconds, setSeconds] = useState(0);
const intervalRef = useRef();
const startTimer = () => {
intervalRef.current = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
};
const stopTimer = () => {
clearInterval(intervalRef.current);
};
useEffect(() => {
return () => clearInterval(intervalRef.current);
}, []);
return (
<div>
<p>Seconds: {seconds}</p>
<button onClick={startTimer}>Start</button>
<button onClick={stopTimer}>Stop</button>
</div>
);
}
// Scroll to Element Example
function ScrollToSection() {
const sectionRef = useRef(null);
const scrollToSection = () => {
sectionRef.current.scrollIntoView({ behavior: 'smooth' });
};
return (
<>
<button onClick={scrollToSection}>Scroll Down</button>
<div style={{ height: '1000px' }}>Content...</div>
<div ref={sectionRef}>Target Section</div>
</>
);
}
🎮 Interactive Demo: useRef Examples
Click Me!
Box 1
Box 1
Click Me!
Box 2
Box 2
Click Count (without re-render):
Box 1: 0 clicks
Box 2: 0 clicks
Box 1: 0 clicks
Box 2: 0 clicks
💡 Notice: Click counts update hote hain lekin component re-render nahi hota! Yeh useRef ki power hai - mutable values without re-renders.
| Feature | useState | useRef |
|---|---|---|
| Re-render | ✅ Triggers re-render | ❌ No re-render |
| Value Access | Direct value | .current property |
| Update | setState function | Direct assignment |
| Use Case | UI state | DOM refs, timers, previous values |
| Persistence | Across re-renders | Across re-renders |
✅ useRef Best Practices:
• DOM manipulation ke liye use karo (focus, scroll, animations)
• Timers aur intervals store karne ke liye
• Previous values track karne ke liye
• Instance variables (class components ke this.variable jaisa)
• Third-party library instances store karne ke liye
❌ Avoid:
• UI state ke liye (use useState instead)
• Ref values ko render mein directly use karna
• Ref values pe conditional rendering
• DOM manipulation ke liye use karo (focus, scroll, animations)
• Timers aur intervals store karne ke liye
• Previous values track karne ke liye
• Instance variables (class components ke this.variable jaisa)
• Third-party library instances store karne ke liye
❌ Avoid:
• UI state ke liye (use useState instead)
• Ref values ko render mein directly use karna
• Ref values pe conditional rendering
3️⃣ useImperativeHandle - Parent-Child Communication
useImperativeHandle ek advanced hook hai jo parent component ko child component ke internal methods expose karne deta hai. Yeh
Normally React mein data flow top-down hota hai (props through), lekin kabhi-kabhi parent ko child ke methods directly call karne padte hain. Tab useImperativeHandle use hota hai.
forwardRef ke saath use hota hai aur parent ko child ke specific functions call karne ki permission deta hai.
Normally React mein data flow top-down hota hai (props through), lekin kabhi-kabhi parent ko child ke methods directly call karne padte hain. Tab useImperativeHandle use hota hai.
// Basic useImperativeHandle Example
import { forwardRef, useImperativeHandle, useRef } from 'react';
// Child Component
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
// Expose specific methods to parent
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
clear: () => {
inputRef.current.value = '';
},
setValue: (value) => {
inputRef.current.value = value;
}
}));
return <input ref={inputRef} {...props} />;
});
// Parent Component
function Parent() {
const inputRef = useRef();
const handleFocus = () => {
inputRef.current.focus();
};
const handleClear = () => {
inputRef.current.clear();
};
return (
<>
<FancyInput ref={inputRef} />
<button onClick={handleFocus}>Focus</button>
<button onClick={handleClear}>Clear</button>
</>
);
}
// Advanced Example - Video Player Control
const VideoPlayer = forwardRef((props, ref) => {
const videoRef = useRef();
const [isPlaying, setIsPlaying] = useState(false);
useImperativeHandle(ref, () => ({
play: () => {
videoRef.current.play();
setIsPlaying(true);
},
pause: () => {
videoRef.current.pause();
setIsPlaying(false);
},
seek: (time) => {
videoRef.current.currentTime = time;
},
getStatus: () => ({
isPlaying,
currentTime: videoRef.current.currentTime,
duration: videoRef.current.duration
})
}));
return <video ref={videoRef} src={props.src} />;
});
// Modal Example
const Modal = forwardRef((props, ref) => {
const [isOpen, setIsOpen] = useState(false);
useImperativeHandle(ref, () => ({
open: () => setIsOpen(true),
close: () => setIsOpen(false),
toggle: () => setIsOpen(prev => !prev)
}));
if (!isOpen) return null;
return (
<div className="modal">
{props.children}
</div>
);
});
🎮 Interactive Demo: useImperativeHandle
Custom Input Component
Output will appear here...
Modal Control
⚠️ Important Notes:
• useImperativeHandle ko sparingly use karo - yeh React ke declarative nature ke against hai
• Sirf jab imperative actions zaroori ho tab use karo (focus, scroll, play/pause)
• Hamesha forwardRef ke saath use hota hai
• Parent-child coupling increase hota hai, so carefully use karo
• Better alternative: Controlled components with props and callbacks
• useImperativeHandle ko sparingly use karo - yeh React ke declarative nature ke against hai
• Sirf jab imperative actions zaroori ho tab use karo (focus, scroll, play/pause)
• Hamesha forwardRef ke saath use hota hai
• Parent-child coupling increase hota hai, so carefully use karo
• Better alternative: Controlled components with props and callbacks
❌ Without useImperativeHandle
Parent ko child ke internal state/methods access nahi hote. Har cheez props through pass karni padti hai, jo complex ho sakta hai.
✅ With useImperativeHandle
Parent directly child ke exposed methods call kar sakta hai. Clean API for imperative actions like focus, play, pause.
4️⃣ useLayoutEffect - Synchronous Side Effects
useLayoutEffect useEffect jaisa hi hai, lekin yeh synchronously fire hota hai DOM mutations ke baad aur browser ke paint karne se pehle. Iska matlab hai ki agar aapko DOM measurements leni hain ya visual changes karne hain before browser paints, toh useLayoutEffect use karo.
// useEffect vs useLayoutEffect
// useEffect - Asynchronous, after paint
useEffect(() => {
// Runs AFTER browser has painted
// User might see flicker if you update DOM here
}, []);
// useLayoutEffect - Synchronous, before paint
useLayoutEffect(() => {
// Runs BEFORE browser paints
// Blocks visual updates until complete
}, []);
// Example: Measuring DOM Elements
function MeasureElement() {
const [height, setHeight] = useState(0);
const elementRef = useRef();
useLayoutEffect(() => {
// Measure before paint - no flicker
const rect = elementRef.current.getBoundingClientRect();
setHeight(rect.height);
}, []);
return (
<div>
<div ref={elementRef}>Content</div>
<p>Height: {height}px</p>
</div>
);
}
// Example: Tooltip Positioning
function Tooltip({ targetRef, children }) {
const [position, setPosition] = useState({ top: 0, left: 0 });
const tooltipRef = useRef();
useLayoutEffect(() => {
const targetRect = targetRef.current.getBoundingClientRect();
const tooltipRect = tooltipRef.current.getBoundingClientRect();
// Calculate position before paint
setPosition({
top: targetRect.bottom + 10,
left: targetRect.left + (targetRect.width - tooltipRect.width) / 2
});
}, [targetRef]);
return (
<div
ref={tooltipRef}
style={{ position: 'absolute', ...position }}
>
{children}
</div>
);
}
// Example: Scroll Position Restoration
function ScrollRestoration() {
const scrollRef = useRef();
useLayoutEffect(() => {
// Restore scroll position before paint
const savedPosition = localStorage.getItem('scrollPosition');
if (savedPosition) {
scrollRef.current.scrollTop = parseInt(savedPosition);
}
}, []);
return <div ref={scrollRef}>Long content...</div>;
}
Render
→
useLayoutEffect
→
Browser Paint
→
useEffect
| Feature | useEffect | useLayoutEffect |
|---|---|---|
| Timing | After paint (async) | Before paint (sync) |
| Blocks Painting | ❌ No | ✅ Yes |
| Use Case | Data fetching, subscriptions | DOM measurements, animations |
| Performance | Better (non-blocking) | Can block if slow |
| Visual Flicker | Possible | Prevented |
🚨 When to Use useLayoutEffect:
✅ Use karo jab:
• DOM measurements leni hain (getBoundingClientRect)
• Visual changes before paint (positioning, animations)
• Scroll position restoration
• Preventing visual flicker is critical
❌ Use mat karo jab:
• Data fetching
• Event listeners
• Subscriptions
• Most side effects (use useEffect instead)
⚠️ Warning: useLayoutEffect blocks browser painting, so heavy operations can make UI feel sluggish. Use sparingly!
✅ Use karo jab:
• DOM measurements leni hain (getBoundingClientRect)
• Visual changes before paint (positioning, animations)
• Scroll position restoration
• Preventing visual flicker is critical
❌ Use mat karo jab:
• Data fetching
• Event listeners
• Subscriptions
• Most side effects (use useEffect instead)
⚠️ Warning: useLayoutEffect blocks browser painting, so heavy operations can make UI feel sluggish. Use sparingly!
5️⃣ Custom Hooks - Reusable Logic
Custom Hooks aapke khud ke hooks hain jo reusable logic encapsulate karte hain. Yeh functions hain jo React hooks use karte hain aur "use" prefix se start hote hain. Custom hooks se aap complex logic ko extract kar sakte ho aur multiple components mein reuse kar sakte ho.
// Custom Hook Example 1: useLocalStorage
function useLocalStorage(key, initialValue) {
// State to store value
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.log(error);
return initialValue;
}
});
// Return wrapped version of useState's setter
const setValue = (value) => {
try {
const valueToStore = value instanceof Function
? value(storedValue)
: value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.log(error);
}
};
return [storedValue, setValue];
}
// Usage
function App() {
const [name, setName] = useLocalStorage('name', 'Guest');
return <input value={name} onChange={(e) => setName(e.target.value)} />;
}
// Custom Hook Example 2: useFetch
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
setLoading(true);
fetch(url)
.then(response => response.json())
.then(data => {
if (!cancelled) {
setData(data);
setLoading(false);
}
})
.catch(error => {
if (!cancelled) {
setError(error);
setLoading(false);
}
});
return () => {
cancelled = true;
};
}, [url]);
return { data, loading, error };
}
// Custom Hook Example 3: useDebounce
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
// Custom Hook Example 4: useWindowSize
function useWindowSize() {
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 () => window.removeEventListener('resize', handleResize);
}, []);
return windowSize;
}
// Custom Hook Example 5: useToggle
function useToggle(initialValue = false) {
const [value, setValue] = useState(initialValue);
const toggle = useCallback(() => {
setValue(v => !v);
}, []);
return [value, toggle];
}
🎮 Interactive Demo: Custom Hooks
useLocalStorage Hook
Stored Value: None
useDebounce Hook
Immediate Value: -
Debounced Value: -
Debounced Value: -
useToggle Hook
OFF
useWindowSize Hook
Width: -px
Height: -px
Height: -px
Resize your browser window to see live updates!
✅ Custom Hooks Best Practices:
• Always start with "use" prefix (useMyHook)
• Extract reusable logic from components
• Keep hooks focused on single responsibility
• Return values in array or object format
• Document parameters and return values
• Handle cleanup in useEffect
• Make hooks composable (hooks can use other hooks)
• Test hooks independently
• Always start with "use" prefix (useMyHook)
• Extract reusable logic from components
• Keep hooks focused on single responsibility
• Return values in array or object format
• Document parameters and return values
• Handle cleanup in useEffect
• Make hooks composable (hooks can use other hooks)
• Test hooks independently
Popular Custom Hooks Examples
| Hook Name | Purpose | Returns |
|---|---|---|
| useLocalStorage | Sync state with localStorage | [value, setValue] |
| useFetch | Data fetching with loading/error states | { data, loading, error } |
| useDebounce | Delay value updates | debouncedValue |
| useWindowSize | Track window dimensions | { width, height } |
| useToggle | Boolean state toggle | [value, toggle] |
| useOnClickOutside | Detect clicks outside element | ref |
| useInterval | Declarative setInterval | void |
| usePrevious | Track previous value | previousValue |
🎯 Interview Questions
Master these questions to ace your Advanced Hooks interviews!
Easy
Q1: useReducer kya hai aur useState se kab better hai? Example ke saath explain karo.
Answer:
useReducer kya hai:
useReducer ek hook hai jo complex state logic manage karne ke liye use hota hai. Yeh Redux pattern follow karta hai - actions dispatch karo aur reducer function state update karta hai.
Syntax:
useState se kab better hai:
useState use karo jab:
• Simple, independent state values
• Ek ya do state variables
• State updates simple hain
useReducer use karo jab:
• Complex state logic with multiple sub-values
• Next state depends on previous state
• Multiple related state updates
• State transitions predictable banana hai
Example:
Benefits:
• Single source of truth
• Predictable state updates
• Easy to test (pure functions)
• Better for complex state logic
• Easier to debug with action logs
useReducer kya hai:
useReducer ek hook hai jo complex state logic manage karne ke liye use hota hai. Yeh Redux pattern follow karta hai - actions dispatch karo aur reducer function state update karta hai.
Syntax:
const [state, dispatch] = useReducer(reducer, initialState);useState se kab better hai:
useState use karo jab:
• Simple, independent state values
• Ek ya do state variables
• State updates simple hain
useReducer use karo jab:
• Complex state logic with multiple sub-values
• Next state depends on previous state
• Multiple related state updates
• State transitions predictable banana hai
Example:
// useState - Multiple states
const [count, setCount] = useState(0);
const [step, setStep] = useState(1);
const [history, setHistory] = useState([]);
// useReducer - Single state object
const [state, dispatch] = useReducer(reducer, {
count: 0,
step: 1,
history: []
});
// Reducer function
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return {
...state,
count: state.count + state.step,
history: [...state.history, state.count + state.step]
};
case 'SET_STEP':
return { ...state, step: action.payload };
default:
return state;
}
}Benefits:
• Single source of truth
• Predictable state updates
• Easy to test (pure functions)
• Better for complex state logic
• Easier to debug with action logs
Easy
Q2: useRef kya hai aur useState se kya difference hai? Kab useRef use karna chahiye?
Answer:
useRef kya hai:
useRef ek hook hai jo mutable value store karta hai jo re-render trigger nahi karta. Yeh ek object return karta hai jiska .current property mutable hai.
Two Main Uses:
1. DOM References: DOM elements ko directly access karna
2. Mutable Values: Values jo re-render nahi karti (timers, previous values)
useState vs useRef:
Kab useRef use karein:
✅ Use karo jab:
• DOM elements ko access karna hai (focus, scroll)
• Timers/intervals store karne hain
• Previous values track karne hain
• Instance variables chahiye (class component ke this.variable jaisa)
• Third-party library instances store karne hain
Examples:
useRef kya hai:
useRef ek hook hai jo mutable value store karta hai jo re-render trigger nahi karta. Yeh ek object return karta hai jiska .current property mutable hai.
Two Main Uses:
1. DOM References: DOM elements ko directly access karna
2. Mutable Values: Values jo re-render nahi karti (timers, previous values)
useState vs useRef:
| Feature | useState | useRef |
|---|---|---|
| Re-render | ✅ Triggers | ❌ No re-render |
| Value Access | Direct | .current property |
| Update | setState function | Direct assignment |
| Use Case | UI state | DOM refs, timers |
Kab useRef use karein:
✅ Use karo jab:
• DOM elements ko access karna hai (focus, scroll)
• Timers/intervals store karne hain
• Previous values track karne hain
• Instance variables chahiye (class component ke this.variable jaisa)
• Third-party library instances store karne hain
Examples:
// DOM Reference
const inputRef = useRef(null);
const focusInput = () => inputRef.current.focus();
// Mutable Value (no re-render)
const countRef = useRef(0);
countRef.current++; // No re-render!
// Timer
const intervalRef = useRef();
intervalRef.current = setInterval(() => {...}, 1000);
// Previous Value
const prevValueRef = useRef();
useEffect(() => {
prevValueRef.current = value;
});
Medium
Q3: useImperativeHandle kya hai? Kab aur kyun use karte hain? forwardRef ke saath kaise kaam karta hai?
Answer:
useImperativeHandle kya hai:
useImperativeHandle ek hook hai jo parent component ko child component ke specific methods expose karne deta hai. Yeh forwardRef ke saath use hota hai aur parent ko child ke internal functions call karne ki permission deta hai.
Kab use karte hain:
• Jab parent ko child ke imperative methods call karne hain
• DOM operations (focus, scroll, play/pause)
• Child component ka internal API expose karna hai
• Controlled components with imperative actions
forwardRef ke saath kaise kaam karta hai:
Real-World Example - Video Player:
⚠️ Important Notes:
• Sparingly use karo - React ke declarative nature ke against hai
• Sirf imperative actions ke liye (focus, play, pause)
• Parent-child coupling increase hota hai
• Better alternative: Controlled components with props
useImperativeHandle kya hai:
useImperativeHandle ek hook hai jo parent component ko child component ke specific methods expose karne deta hai. Yeh forwardRef ke saath use hota hai aur parent ko child ke internal functions call karne ki permission deta hai.
Kab use karte hain:
• Jab parent ko child ke imperative methods call karne hain
• DOM operations (focus, scroll, play/pause)
• Child component ka internal API expose karna hai
• Controlled components with imperative actions
forwardRef ke saath kaise kaam karta hai:
// Step 1: forwardRef se child component wrap karo
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
// Step 2: useImperativeHandle se methods expose karo
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
clear: () => {
inputRef.current.value = '';
},
setValue: (value) => {
inputRef.current.value = value;
}
}));
return <input ref={inputRef} {...props} />;
});
// Step 3: Parent mein use karo
function Parent() {
const inputRef = useRef();
const handleFocus = () => {
inputRef.current.focus(); // Call child method
};
return (
<>
<FancyInput ref={inputRef} />
<button onClick={handleFocus}>Focus</button>
</>
);
}Real-World Example - Video Player:
const VideoPlayer = forwardRef((props, ref) => {
const videoRef = useRef();
useImperativeHandle(ref, () => ({
play: () => videoRef.current.play(),
pause: () => videoRef.current.pause(),
seek: (time) => videoRef.current.currentTime = time,
getStatus: () => ({
currentTime: videoRef.current.currentTime,
duration: videoRef.current.duration
})
}));
return <video ref={videoRef} src={props.src} />;
});⚠️ Important Notes:
• Sparingly use karo - React ke declarative nature ke against hai
• Sirf imperative actions ke liye (focus, play, pause)
• Parent-child coupling increase hota hai
• Better alternative: Controlled components with props
Medium
Q4: useLayoutEffect aur useEffect mein kya difference hai? Kab useLayoutEffect use karna chahiye?
Answer:
Main Difference:
• useEffect: Asynchronously runs AFTER browser paint
• useLayoutEffect: Synchronously runs BEFORE browser paint
Execution Order:
1. Component renders
2. useLayoutEffect runs (blocks painting)
3. Browser paints screen
4. useEffect runs (non-blocking)
Detailed Comparison:
Kab useLayoutEffect use karein:
✅ Use karo jab:
• DOM measurements leni hain (getBoundingClientRect)
• Visual changes before paint (positioning, animations)
• Scroll position restoration
• Tooltip/popover positioning
• Preventing visual flicker is critical
❌ Use mat karo jab:
• Data fetching
• Event listeners
• Subscriptions
• Most side effects (use useEffect)
Examples:
⚠️ Warning:
useLayoutEffect blocks browser painting, so heavy operations can make UI sluggish. Use sparingly and only when necessary!
Main Difference:
• useEffect: Asynchronously runs AFTER browser paint
• useLayoutEffect: Synchronously runs BEFORE browser paint
Execution Order:
1. Component renders
2. useLayoutEffect runs (blocks painting)
3. Browser paints screen
4. useEffect runs (non-blocking)
Detailed Comparison:
| Feature | useEffect | useLayoutEffect |
|---|---|---|
| Timing | After paint | Before paint |
| Blocks Paint | ❌ No | ✅ Yes |
| Execution | Async | Sync |
| Performance | Better | Can block |
| Visual Flicker | Possible | Prevented |
Kab useLayoutEffect use karein:
✅ Use karo jab:
• DOM measurements leni hain (getBoundingClientRect)
• Visual changes before paint (positioning, animations)
• Scroll position restoration
• Tooltip/popover positioning
• Preventing visual flicker is critical
❌ Use mat karo jab:
• Data fetching
• Event listeners
• Subscriptions
• Most side effects (use useEffect)
Examples:
// ❌ useEffect - Might see flicker
useEffect(() => {
const rect = elementRef.current.getBoundingClientRect();
setHeight(rect.height); // User might see jump
}, []);
// ✅ useLayoutEffect - No flicker
useLayoutEffect(() => {
const rect = elementRef.current.getBoundingClientRect();
setHeight(rect.height); // Smooth, no jump
}, []);
// Tooltip positioning
useLayoutEffect(() => {
const targetRect = targetRef.current.getBoundingClientRect();
const tooltipRect = tooltipRef.current.getBoundingClientRect();
// Calculate position before paint
setPosition({
top: targetRect.bottom + 10,
left: targetRect.left
});
}, []);⚠️ Warning:
useLayoutEffect blocks browser painting, so heavy operations can make UI sluggish. Use sparingly and only when necessary!
Hard
Q5: Custom hooks kaise banate hain? Best practices kya hain? Real-world example ke saath explain karo.
Answer:
Custom Hooks kaise banate hain:
Custom hooks simple JavaScript functions hain jo React hooks use karte hain aur "use" prefix se start hote hain. Yeh reusable logic encapsulate karte hain.
Basic Structure:
Real-World Example - useFetch:
Advanced Example - useForm:
Best Practices:
1. Naming:
• Always start with "use" prefix
• Descriptive names (useAuth, useFetch, useForm)
2. Return Values:
• Array for 2 values: [value, setValue]
• Object for multiple values: { data, loading, error }
3. Dependencies:
• Properly manage useEffect dependencies
• Cleanup functions for subscriptions/timers
4. Composition:
• Hooks can use other hooks
• Keep hooks focused (single responsibility)
5. Error Handling:
• Handle errors gracefully
• Provide error states
6. Testing:
• Test hooks independently
• Use @testing-library/react-hooks
Common Custom Hooks:
• useLocalStorage - Sync with localStorage
• useDebounce - Delay value updates
• useWindowSize - Track window dimensions
• useOnClickOutside - Detect outside clicks
• useInterval - Declarative setInterval
• usePrevious - Track previous value
• useToggle - Boolean state toggle
• useAsync - Async operation handling
Custom Hooks kaise banate hain:
Custom hooks simple JavaScript functions hain jo React hooks use karte hain aur "use" prefix se start hote hain. Yeh reusable logic encapsulate karte hain.
Basic Structure:
function useCustomHook(params) {
// 1. Use React hooks
const [state, setState] = useState(initialValue);
// 2. Add logic
useEffect(() => {
// Side effects
}, [dependencies]);
// 3. Return values
return [state, setState];
}Real-World Example - useFetch:
function useFetch(url, options = {}) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const json = await response.json();
if (!cancelled) {
setData(json);
setError(null);
}
} catch (err) {
if (!cancelled) {
setError(err.message);
setData(null);
}
} finally {
if (!cancelled) {
setLoading(false);
}
}
};
fetchData();
return () => {
cancelled = true;
};
}, [url, JSON.stringify(options)]);
return { data, loading, error };
}
// Usage
function UserProfile({ userId }) {
const { data, loading, error } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{data.name}</div>;
}Advanced Example - useForm:
function useForm(initialValues, validate) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleChange = (e) => {
const { name, value } = e.target;
setValues(prev => ({ ...prev, [name]: value }));
// Clear error when user types
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: null }));
}
};
const handleBlur = (e) => {
const { name } = e.target;
setTouched(prev => ({ ...prev, [name]: true }));
// Validate on blur
if (validate) {
const fieldError = validate(name, values[name]);
if (fieldError) {
setErrors(prev => ({ ...prev, [name]: fieldError }));
}
}
};
const handleSubmit = async (onSubmit) => {
return async (e) => {
e.preventDefault();
setIsSubmitting(true);
// Validate all fields
if (validate) {
const validationErrors = {};
Object.keys(values).forEach(key => {
const error = validate(key, values[key]);
if (error) validationErrors[key] = error;
});
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
setIsSubmitting(false);
return;
}
}
try {
await onSubmit(values);
} catch (error) {
setErrors({ submit: error.message });
} finally {
setIsSubmitting(false);
}
};
};
const reset = () => {
setValues(initialValues);
setErrors({});
setTouched({});
setIsSubmitting(false);
};
return {
values,
errors,
touched,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
reset
};
}Best Practices:
1. Naming:
• Always start with "use" prefix
• Descriptive names (useAuth, useFetch, useForm)
2. Return Values:
• Array for 2 values: [value, setValue]
• Object for multiple values: { data, loading, error }
3. Dependencies:
• Properly manage useEffect dependencies
• Cleanup functions for subscriptions/timers
4. Composition:
• Hooks can use other hooks
• Keep hooks focused (single responsibility)
5. Error Handling:
• Handle errors gracefully
• Provide error states
6. Testing:
• Test hooks independently
• Use @testing-library/react-hooks
Common Custom Hooks:
• useLocalStorage - Sync with localStorage
• useDebounce - Delay value updates
• useWindowSize - Track window dimensions
• useOnClickOutside - Detect outside clicks
• useInterval - Declarative setInterval
• usePrevious - Track previous value
• useToggle - Boolean state toggle
• useAsync - Async operation handling
Hard
Q6: useReducer aur Redux mein kya difference hai? Production app mein kab kaunsa use karein?
Answer:
useReducer vs Redux:
useReducer Example:
Redux Example:
Kab kaunsa use karein:
useReducer use karo jab:
✅ Component-level complex state
✅ State sirf ek component tree mein chahiye
✅ Small to medium applications
✅ No need for middleware
✅ Want to keep bundle size small
✅ Simple state management
Redux use karo jab:
✅ Global state across entire app
✅ Multiple components need same state
✅ Large, complex applications
✅ Need middleware (async actions, logging)
✅ Want Redux DevTools for debugging
✅ Time-travel debugging needed
✅ Team familiar with Redux patterns
Hybrid Approach:
Modern Alternative - Context + useReducer:
Decision Tree:
1. Need global state? → Yes → Redux or Context+useReducer
2. Need middleware? → Yes → Redux
3. Need DevTools? → Yes → Redux
4. Component-level only? → Yes → useReducer
5. Simple state? → Yes → useState
Recommendation:
• Small apps: useState + useReducer
• Medium apps: Context + useReducer
• Large apps: Redux or Zustand
• Enterprise: Redux with Redux Toolkit
useReducer vs Redux:
| Feature | useReducer | Redux |
|---|---|---|
| Scope | Component level | Application level |
| Setup | Built-in, no installation | External library |
| DevTools | ❌ No | ✅ Redux DevTools |
| Middleware | ❌ No | ✅ Yes (thunk, saga) |
| Time Travel | ❌ No | ✅ Yes |
| Bundle Size | 0 KB (built-in) | ~10 KB |
| Learning Curve | Easy | Moderate |
useReducer Example:
// Component-level state
function TodoList() {
const [state, dispatch] = useReducer(todoReducer, initialState);
// State is local to this component
return (
<div>
{state.todos.map(todo =>
<Todo key={todo.id} {...todo} />
)}
</div>
);
}Redux Example:
// Global state accessible anywhere
import { useSelector, useDispatch } from 'react-redux';
function TodoList() {
const todos = useSelector(state => state.todos);
const dispatch = useDispatch();
// State is global, accessible from any component
return (
<div>
{todos.map(todo =>
<Todo key={todo.id} {...todo} />
)}
</div>
);
}Kab kaunsa use karein:
useReducer use karo jab:
✅ Component-level complex state
✅ State sirf ek component tree mein chahiye
✅ Small to medium applications
✅ No need for middleware
✅ Want to keep bundle size small
✅ Simple state management
Redux use karo jab:
✅ Global state across entire app
✅ Multiple components need same state
✅ Large, complex applications
✅ Need middleware (async actions, logging)
✅ Want Redux DevTools for debugging
✅ Time-travel debugging needed
✅ Team familiar with Redux patterns
Hybrid Approach:
// Use both together!
// Redux for global state (user, auth, theme)
// useReducer for local complex state (forms, modals)
function ComplexForm() {
// Local form state with useReducer
const [formState, dispatch] = useReducer(formReducer, initialState);
// Global user state from Redux
const user = useSelector(state => state.user);
return <form>...</form>;
}Modern Alternative - Context + useReducer:
// Create global state without Redux
const StateContext = createContext();
const DispatchContext = createContext();
function StateProvider({ children }) {
const [state, dispatch] = useReducer(rootReducer, initialState);
return (
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>
{children}
</DispatchContext.Provider>
</StateContext.Provider>
);
}
// Custom hooks for easy access
function useState() {
return useContext(StateContext);
}
function useDispatch() {
return useContext(DispatchContext);
}Decision Tree:
1. Need global state? → Yes → Redux or Context+useReducer
2. Need middleware? → Yes → Redux
3. Need DevTools? → Yes → Redux
4. Component-level only? → Yes → useReducer
5. Simple state? → Yes → useState
Recommendation:
• Small apps: useState + useReducer
• Medium apps: Context + useReducer
• Large apps: Redux or Zustand
• Enterprise: Redux with Redux Toolkit
Hard
Q7: Production-ready custom hook kaise banayein? Error handling, cleanup, aur optimization ke saath complete example do.
Answer:
Production-Ready Custom Hook - useAsync:
Advanced Example - useWebSocket:
Production Best Practices:
1. Error Handling:
• Try-catch blocks for all async operations
• Provide error states and messages
• Handle edge cases (network errors, timeouts)
2. Cleanup:
• Always cleanup in useEffect return
• Cancel pending requests on unmount
• Clear timers and intervals
• Close connections (WebSocket, SSE)
3. Memory Leaks Prevention:
• Use mounted ref to prevent setState on unmounted component
• Cancel async operations on unmount
• Remove event listeners
4. Optimization:
• Use useCallback for functions
• Use useMemo for expensive computations
• Proper dependency arrays
• Debounce/throttle when needed
5. TypeScript Support:
6. Testing:
7. Documentation:
• JSDoc comments
• Usage examples
• Parameter descriptions
• Return value documentation
Checklist:
☑️ Error handling implemented
☑️ Cleanup functions added
☑️ Memory leak prevention
☑️ TypeScript types defined
☑️ Tests written
☑️ Documentation added
☑️ Performance optimized
☑️ Edge cases handled
Production-Ready Custom Hook - useAsync:
import { useState, useEffect, useCallback, useRef } from 'react';
function useAsync(asyncFunction, immediate = true) {
// State management
const [status, setStatus] = useState('idle');
const [data, setData] = useState(null);
const [error, setError] = useState(null);
// Refs for cleanup and preventing memory leaks
const mountedRef = useRef(true);
const pendingPromiseRef = useRef(null);
// Execute async function
const execute = useCallback((...params) => {
setStatus('pending');
setData(null);
setError(null);
// Store promise for potential cancellation
const promise = asyncFunction(...params);
pendingPromiseRef.current = promise;
return promise
.then(response => {
// Only update if component is still mounted
if (mountedRef.current) {
setData(response);
setStatus('success');
}
return response;
})
.catch(error => {
if (mountedRef.current) {
setError(error);
setStatus('error');
}
throw error;
})
.finally(() => {
if (mountedRef.current) {
pendingPromiseRef.current = null;
}
});
}, [asyncFunction]);
// Reset function
const reset = useCallback(() => {
if (mountedRef.current) {
setStatus('idle');
setData(null);
setError(null);
}
}, []);
// Execute immediately if requested
useEffect(() => {
if (immediate) {
execute();
}
}, [execute, immediate]);
// Cleanup on unmount
useEffect(() => {
return () => {
mountedRef.current = false;
};
}, []);
// Return values and functions
return {
execute,
reset,
status,
data,
error,
isIdle: status === 'idle',
isPending: status === 'pending',
isSuccess: status === 'success',
isError: status === 'error'
};
}
// Usage Example
function UserProfile({ userId }) {
const fetchUser = useCallback(
() => fetch(`/api/users/${userId}`).then(r => r.json()),
[userId]
);
const {
data: user,
error,
isPending,
isError,
execute: refetch
} = useAsync(fetchUser);
if (isPending) return <Spinner />;
if (isError) return <Error error={error} onRetry={refetch} />;
return (
<div>
<h1>{user.name}</h1>
<button onClick={refetch}>Refresh</button>
</div>
);
}Advanced Example - useWebSocket:
function useWebSocket(url, options = {}) {
const [data, setData] = useState(null);
const [status, setStatus] = useState('connecting');
const [error, setError] = useState(null);
const wsRef = useRef(null);
const reconnectTimeoutRef = useRef(null);
const reconnectAttemptsRef = useRef(0);
const {
reconnect = true,
reconnectInterval = 3000,
maxReconnectAttempts = 5,
onOpen,
onClose,
onError,
onMessage
} = options;
// Send message function
const sendMessage = useCallback((message) => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(
typeof message === 'string' ? message : JSON.stringify(message)
);
return true;
}
return false;
}, []);
// Connect function
const connect = useCallback(() => {
try {
setStatus('connecting');
const ws = new WebSocket(url);
ws.onopen = (event) => {
setStatus('connected');
setError(null);
reconnectAttemptsRef.current = 0;
onOpen?.(event);
};
ws.onmessage = (event) => {
try {
const parsedData = JSON.parse(event.data);
setData(parsedData);
onMessage?.(parsedData);
} catch {
setData(event.data);
onMessage?.(event.data);
}
};
ws.onerror = (event) => {
setError(new Error('WebSocket error'));
setStatus('error');
onError?.(event);
};
ws.onclose = (event) => {
setStatus('disconnected');
onClose?.(event);
// Attempt reconnection
if (
reconnect &&
reconnectAttemptsRef.current < maxReconnectAttempts
) {
reconnectAttemptsRef.current++;
reconnectTimeoutRef.current = setTimeout(() => {
connect();
}, reconnectInterval);
}
};
wsRef.current = ws;
} catch (err) {
setError(err);
setStatus('error');
}
}, [url, reconnect, reconnectInterval, maxReconnectAttempts, onOpen, onClose, onError, onMessage]);
// Disconnect function
const disconnect = useCallback(() => {
if (reconnectTimeoutRef.current) {
clearTimeout(reconnectTimeoutRef.current);
}
if (wsRef.current) {
wsRef.current.close();
wsRef.current = null;
}
}, []);
// Connect on mount
useEffect(() => {
connect();
return () => {
disconnect();
};
}, [connect, disconnect]);
return {
data,
status,
error,
sendMessage,
connect,
disconnect,
isConnecting: status === 'connecting',
isConnected: status === 'connected',
isDisconnected: status === 'disconnected',
isError: status === 'error'
};
}Production Best Practices:
1. Error Handling:
• Try-catch blocks for all async operations
• Provide error states and messages
• Handle edge cases (network errors, timeouts)
2. Cleanup:
• Always cleanup in useEffect return
• Cancel pending requests on unmount
• Clear timers and intervals
• Close connections (WebSocket, SSE)
3. Memory Leaks Prevention:
• Use mounted ref to prevent setState on unmounted component
• Cancel async operations on unmount
• Remove event listeners
4. Optimization:
• Use useCallback for functions
• Use useMemo for expensive computations
• Proper dependency arrays
• Debounce/throttle when needed
5. TypeScript Support:
function useAsync<T>(
asyncFunction: (...args: any[]) => Promise<T>,
immediate = true
): {
execute: (...args: any[]) => Promise<T>;
reset: () => void;
status: 'idle' | 'pending' | 'success' | 'error';
data: T | null;
error: Error | null;
isIdle: boolean;
isPending: boolean;
isSuccess: boolean;
isError: boolean;
} {
// Implementation...
}6. Testing:
import { renderHook, act } from '@testing-library/react-hooks';
describe('useAsync', () => {
it('should handle successful async operation', async () => {
const asyncFn = jest.fn().mockResolvedValue('data');
const { result, waitForNextUpdate } = renderHook(() =>
useAsync(asyncFn)
);
expect(result.current.isPending).toBe(true);
await waitForNextUpdate();
expect(result.current.isSuccess).toBe(true);
expect(result.current.data).toBe('data');
});
it('should handle errors', async () => {
const error = new Error('Failed');
const asyncFn = jest.fn().mockRejectedValue(error);
const { result, waitForNextUpdate } = renderHook(() =>
useAsync(asyncFn)
);
await waitForNextUpdate();
expect(result.current.isError).toBe(true);
expect(result.current.error).toBe(error);
});
});7. Documentation:
• JSDoc comments
• Usage examples
• Parameter descriptions
• Return value documentation
Checklist:
☑️ Error handling implemented
☑️ Cleanup functions added
☑️ Memory leak prevention
☑️ TypeScript types defined
☑️ Tests written
☑️ Documentation added
☑️ Performance optimized
☑️ Edge cases handled