๐ก Key Insight: Patterns sikhne se aap better architect ban sakte ho aur production-ready code likh sakte ho jo industry standards follow karta hai.
๐ญ Higher-Order Components
Components ko wrap karke extra functionality add karo
๐ช Render Props
Function as prop pass karke rendering logic share karo
๐งฉ Compound Components
Related components ko group karke implicit state share karo
๐ฎ Controlled vs Uncontrolled
Form state management ke different approaches
๐ Provider Pattern
Context API se global state management
โ๏ธ State Reducer Pattern
Complex state logic ko control karo
๐ป Headless Components
Logic aur UI ko separate karo
๐ญ Higher-Order Components (HOCs)
Higher-Order Component (HOC) ek function hai jo ek component leta hai aur ek naya enhanced component return karta hai. Ye component reusability ke liye bahut powerful pattern hai.
Simple words mein: Ek wrapper function jo aapke component ko superpowers deta hai! ๐ฆธโโ๏ธ
Definition: A Higher-Order Component is a function that takes a component and returns a new component with additional props or behavior.
Basic HOC Example
// withLoading HOC - Loading functionality add karta hai
function withLoading(Component) {
return function WithLoadingComponent({ isLoading, ...props }) {
if (isLoading) {
return <div>Loading...</div>;
}
return <Component {...props} />;
};
}
// Original Component
function UserList({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// Enhanced Component with loading
const UserListWithLoading = withLoading(UserList);
// Usage
<UserListWithLoading isLoading={true} users={[]} />
Advanced HOC - Authentication
// withAuth HOC - Authentication check karta hai
function withAuth(Component) {
return function WithAuthComponent(props) {
const { isAuthenticated, user } = useAuth();
if (!isAuthenticated) {
return <Navigate to="/login" />;
}
return <Component {...props} user={user} />;
};
}
// Protected Component
function Dashboard({ user }) {
return <h1>Welcome, {user.name}!</h1>;
}
// Enhanced with authentication
const ProtectedDashboard = withAuth(Dashboard);
Render Props ek technique hai jisme aap ek function ko prop ke through pass karte ho jo batata hai ki component ko kya render karna hai. Ye pattern logic sharing ke liye bahut useful hai.
Simple analogy: Aap chef ko recipe dete ho (function), aur chef dish banata hai (render)! ๐จโ๐ณ
Definition: A component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic.
Compound Components ek pattern hai jisme multiple components milkar ek single functionality provide karte hain. Ye components implicit state share karte hain aur ek dusre ke saath communicate karte hain.
Real-world example: HTML ke <select> aur <option> tags ki tarah! ๐ฏ
Definition: Compound components are a set of components that work together to form a complete UI, sharing implicit state through React Context.
Uncontrolled Component mein form data DOM ke through handle hota hai. Refs use karke values access karte hain.
// Uncontrolled Component Example
function UncontrolledForm() {
const nameRef = useRef();
const emailRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
console.log({
name: nameRef.current.value,
email: emailRef.current.value
});
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
ref={nameRef}
defaultValue=""
placeholder="Name"
/>
<input
type="email"
ref={emailRef}
defaultValue=""
placeholder="Email"
/>
<button type="submit">Submit</button>
</form>
);
}
// Benefits:
// โ Less code
// โ Better performance (no re-renders)
// โ Easy integration with non-React code
// โ Good for simple forms
๐ฎ Controlled vs Uncontrolled Demo
Controlled Form
Current Value: ""
Character Count: 0
โ Real-time validation possible
Uncontrolled Form
Value: Not retrieved yet
โ ๏ธ Value only accessed when needed
Feature
Controlled
Uncontrolled
State Location
React State
DOM
Access Method
State variable
Refs
Re-renders
On every change
No re-renders
Validation
Real-time
On submit
Performance
Slower (more renders)
Faster
Use Case
Complex forms, validation
Simple forms, file uploads
When to Use What?
Controlled Components use karo jab:
Real-time validation chahiye
Conditional logic based on input
Dynamic form fields
Format enforcement (phone, credit card)
Uncontrolled Components use karo jab:
Simple forms with minimal logic
File uploads
Integration with non-React code
Performance critical scenarios
๐ Provider Pattern
Provider Pattern React Context API use karke global state management provide karta hai. Ye prop drilling problem ko solve karta hai.
Analogy: Ek central warehouse se sabko supplies milti hain, har level pe pass karne ki zarurat nahi! ๐ฆ
Definition: Provider Pattern uses React Context to make data available to all components in a tree without explicitly passing props through every level.
Basic Provider Implementation
// Theme Provider Example
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
const value = {
theme,
toggleTheme
};
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
// Custom Hook for easy access
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// Usage in App
function App() {
return (
<ThemeProvider>
<Header />
<Main />
<Footer />
</ThemeProvider>
);
}
// Usage in any nested component
function Header() {
const { theme, toggleTheme } = useTheme();
return (
<header className={theme}>
<button onClick={toggleTheme}>
Toggle Theme
</button>
</header>
);
}
Advanced Provider - Auth Example
// Authentication Provider
const AuthContext = createContext();
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Check if user is logged in
const token = localStorage.getItem('token');
if (token) {
fetchUser(token).then(setUser).finally(() => setLoading(false));
} else {
setLoading(false);
}
}, []);
const login = async (credentials) => {
const { user, token } = await api.login(credentials);
localStorage.setItem('token', token);
setUser(user);
};
const logout = () => {
localStorage.removeItem('token');
setUser(null);
};
const value = {
user,
loading,
login,
logout,
isAuthenticated: !!user
};
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}
// Protected Route Component
function ProtectedRoute({ children }) {
const { isAuthenticated, loading } = useAuth();
if (loading) return <Spinner />;
if (!isAuthenticated) return <Navigate to="/login" />;
return children;
}
๐ฎ Interactive Provider Pattern Demo
Current Theme:Light
Sample Content
This content changes based on the theme from Provider!
No prop drilling needed - any component can access theme.
Split contexts: Ek hi context mein sab kuch mat dalo
Memoize value: useMemo use karo context value ke liye
Separate state: Frequently changing aur stable state ko alag rakho
Use selectors: Sirf required data access karo
โ๏ธ State Reducer Pattern
State Reducer Pattern users ko state management logic ko control karne ki power deta hai. Ye pattern Redux se inspired hai aur complex state logic ke liye perfect hai.
Power: Component ka behavior customize kar sakte ho without modifying component code! ๐๏ธ
Definition: State Reducer Pattern allows users to invert control over state management by providing their own reducer function to override default behavior.
Jab users ko component behavior customize karni ho
Complex state transitions handle karne ke liye
Library/framework components banate waqt
Jab default behavior sufficient nahi hai
๐ป Headless Components Pattern
Headless Components wo components hain jo logic provide karte hain but UI nahi. Ye pattern maximum flexibility deta hai styling aur structure ke liye.
Philosophy: "Bring your own UI" - Logic hum denge, design tum karo! ๐จ
Definition: Headless components provide functionality and behavior without prescribing any specific UI, allowing complete control over rendering and styling.
3. Controlled vs Uncontrolled components - performance implications kya hain?
Mediumโผ
Answer:
Controlled Components:
โ Performance Impact: Har keystroke pe component re-render hota hai
โ Large forms mein slow ho sakta hai
โ Real-time validation possible
โ Conditional logic easy
Uncontrolled Components:
โ Better Performance: No re-renders on input change
โ Large forms ke liye better
โ Validation sirf submit pe
โ Dynamic behavior mushkil
Performance Optimization Tips:
// 1. Debouncing for controlled inputs
const [value, setValue] = useState('');
const debouncedValue = useDebounce(value, 300);
// 2. Use React Hook Form (best of both worlds)
const { register } = useForm();
<input {...register('email')} />
// 3. Split large forms into smaller components
// Each section manages its own state
// 4. Use uncontrolled for simple forms
const nameRef = useRef();
// Access only on submit
Recommendation: Modern apps mein React Hook Form use karo - ye uncontrolled approach use karta hai but controlled jaise features deta hai!
Provider Pattern mein main problem ye hai ki context value change hone pe saare consumers re-render ho jate hain, chahe unhe us value ki zarurat ho ya nahi.
Solutions:
1. Split Contexts (Most Important!):
// โ Bad - Everything in one context
const AppContext = createContext();
function AppProvider({ children }) {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('light');
const [notifications, setNotifications] = useState([]);
// Theme change pe sab re-render!
}
// โ Good - Separate contexts
const UserContext = createContext();
const ThemeContext = createContext();
const NotificationContext = createContext();
// Ab sirf relevant components re-render honge
2. Memoize Context Value:
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
// โ Memoize to prevent unnecessary re-renders
const value = useMemo(() => ({
theme,
setTheme
}), [theme]);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
3. Use Selectors:
// Custom hook with selector
function useUser(selector = (state) => state) {
const context = useContext(UserContext);
return selector(context);
}
// Usage - only re-render when name changes
function UserName() {
const name = useUser(state => state.user.name);
return <div>{name}</div>;
}
4. Separate State and Dispatch:
const StateContext = createContext();
const DispatchContext = createContext();
// Components jo sirf dispatch use karte hain
// wo state changes pe re-render nahi honge
function Button() {
const dispatch = useContext(DispatchContext);
// No re-render on state change!
}
Best Practice: Frequently changing state aur stable state ko alag contexts mein rakho!
5. State Reducer Pattern ka real-world use case explain karo.
Hardโผ
Answer:
Real-World Scenario: Ek reusable Modal component jo different apps mein different behavior chahiye.
// Modal with State Reducer
function useModal({
initialOpen = false,
reducer = (state, action) => action.changes
}) {
const [state, dispatch] = useReducer((state, action) => {
const changes = (() => {
switch (action.type) {
case 'OPEN':
return { isOpen: true };
case 'CLOSE':
return { isOpen: false };
case 'TOGGLE':
return { isOpen: !state.isOpen };
default:
return state;
}
})();
// User's custom reducer can override
return reducer(state, { ...action, changes }) ?? changes;
}, { isOpen: initialOpen });
return {
isOpen: state.isOpen,
open: () => dispatch({ type: 'OPEN' }),
close: () => dispatch({ type: 'CLOSE' }),
toggle: () => dispatch({ type: 'TOGGLE' })
};
}
// Use Case 1: Normal Modal (Default behavior)
function NormalModal() {
const { isOpen, open, close } = useModal();
return (
<>
<button onClick={open}>Open</button>
{isOpen && <Modal onClose={close} />}
</>
);
}
// Use Case 2: Confirmation Required Modal
function ConfirmModal() {
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(true);
const { isOpen, open, close } = useModal({
reducer: (state, action) => {
// Prevent closing if unsaved changes
if (action.type === 'CLOSE' && hasUnsavedChanges) {
const confirmed = window.confirm('Unsaved changes. Close anyway?');
if (!confirmed) {
return state; // Don't close
}
}
return action.changes; // Use default behavior
}
});
return (
<>
<button onClick={open}>Open</button>
{isOpen && (
<Modal onClose={close}>
<input onChange={() => setHasUnsavedChanges(true)} />
</Modal>
)}
</>
);
}
// Use Case 3: Analytics Tracking Modal
function AnalyticsModal() {
const { isOpen, open, close } = useModal({
reducer: (state, action) => {
// Track every modal interaction
if (action.type === 'OPEN') {
analytics.track('Modal Opened');
}
if (action.type === 'CLOSE') {
analytics.track('Modal Closed');
}
return action.changes;
}
});
return <Modal isOpen={isOpen} onClose={close} />;
}
Benefits:
Same component, different behaviors
No need to modify original component
Users control the logic
Perfect for library authors
6. Headless Components ka main advantage kya hai? Kab use nahi karna chahiye?
Mediumโผ
Answer:
Main Advantages:
๐จ Complete Styling Freedom: Apni design system use kar sakte ho
โฟ Accessibility Built-in: Logic mein accessibility handle hoti hai
๐ Maximum Reusability: Same logic, infinite UIs
๐งช Easy Testing: Logic aur UI separately test kar sakte ho
๐ฆ Framework Agnostic: Logic ko kahi bhi use kar sakte ho
When NOT to use:
โ Simple components jo rarely customize hote hain