Data flow advanced patterns
We’ll cover 3 important patterns:
✅ Controlled Components (Forms)
✅ Callback Pattern (Child → Parent communication)
✅ Context API (avoid prop drilling completely)
✅ 1. Controlled Components (Form Handling Pattern)
👉 Use when: You want parent to fully control form data (very common in real apps)
💡 Example
import React, { useState } from "react";
function Form() {
const [name, setName] = useState("");
return (
<div>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter name"
/>
<p>Typed: {name}</p>
</div>
);
}
✅ What’s happening?
Parent (
Form) owns stateInput is controlled by parent
UI always reflects state
🎯 When to use this
✅ Forms
✅ Input validation
✅ Live preview
🧠 Real-life analogy
📋 Google Form:
You type → system stores value
Display updates instantly from same source
✅ 2. Callback Pattern (Child → Parent Communication)
👉 Use when: Child collects data but parent needs to store/use it
💡 Example: Child sends data to Parent
import React, { useState } from "react";
function Parent() {
const [task, setTask] = useState("");
const handleAddTask = (newTask) => {
setTask(newTask);
};
return (
<div>
<Child onAdd={handleAddTask} />
<p>Latest Task: {task}</p>
</div>
);
}
function Child({ onAdd }) {
const [input, setInput] = useState("");
return (
<div>
<input onChange={(e) => setInput(e.target.value)} />
<button onClick={() => onAdd(input)}>Add</button>
</div>
);
}
✅ What’s happening?
Parent gives function (
onAdd)Child collects data
Child calls function
Parent updates state
🎯 When to use this
✅ Forms with submit buttons
✅ Modals sending data
✅ Child triggers parent updates
🧠 Real-life analogy
📞 Customer support:
Customer (child) provides info
Agent (parent) updates system
✅ 3. Context API (Avoid Prop Drilling)
👉 Use when: Many components need same data
👉 Best alternative to prop drilling
💡 Example: Global User Data
Step 1: Create Context
import React, { createContext, useState } from "react";
export const UserContext = createContext();
Step 2: Provide Data
function App() {
const [user, setUser] = useState("Pushpesh");
return (
<UserContext.Provider value={user}>
<Child />
</UserContext.Provider>
);
}
Step 3: Consume Data (Deep Child)
import React, { useContext } from "react";
import { UserContext } from "./UserContext";
function Child() {
const user = useContext(UserContext);
return <h1>Hello {user}</h1>;
}
✅ What’s happening?
No props needed
Any component can access data directly
🎯 When to use this
✅ Authentication (current user)
✅ Theme (dark/light mode)
✅ Language settings
✅ Global app data
🧠 Real-life analogy
📡 WiFi:
Router (Context Provider) broadcasts signal
Any device (component) connects directly
No need to pass cables (props)
🔥 4. Bonus: Context + Callback (Real Production Pattern)
👉 Advanced combination used in real apps
💡 Example
export const ThemeContext = React.createContext();
function App() {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Child />
</ThemeContext.Provider>
);
}
function Child() {
const { theme, setTheme } = React.useContext(ThemeContext);
return (
<button onClick={() => setTheme("dark")}>
Current: {theme}
</button>
);
}
✅ Why this is powerful?
Global data ✅
Global update ✅
No prop drilling ✅
🧭 When to Use What (Super Important)
| Situation | Pattern |
|---|---|
| Simple parent → child | Props |
| Child triggers parent update | Callback |
| Form control | Controlled component |
| Shared data across many components | Context API |
🚀 Real Developer Thinking
When building UI, ask:
✅ "Is this form input?" → Controlled pattern
✅ "Child sending data up?" → Callback
✅ "Too many layers?" → Context
🧠 Final Mental Model
👉 React is like a system of:
📦 Props → data flow down
📞 Callbacks → communication up
📡 Context → global access