React
Chapter 03: Props & Component Communication — Passing and destructuring props, immutability, callback functions, child-to-parent communication, and default values.
1. What are Props?
Props (short for properties) are the mechanism by which parent components pass data and configuration down to child components. Just as you pass arguments to a regular JavaScript function to customize its output, you pass props to a React component to customize its rendered UI.
💡 Real-World Analogy: Custom Order Receipts & Function Parameters
Think of a component as a coffee barista function (brewCoffee(type, milk, sugar)):
- The barista instructions (component definition) stay the exact same.
- The customer (parent component) passes a custom order receipt (props):
<CoffeeCup type="Espresso" milk="Oat" sugar={1} />. - The barista delivers a customized drink without changing their standard brewing technique!
Function Arguments vs React Props
// 1. Standard JavaScript Function:
function buildUserCard(name, role, rating) {
return `${name} - ${role} (${rating}★)`;
}
buildUserCard("Anshuman", "Architect", 5);
// 2. React Functional Component with Props:
function UserCard(props) {
return (
<div className="user-card">
<h3>{props.name}</h3>
<p>Role: {props.role}</p>
<span>Rating: {props.rating}★</span>
</div>
);
}
// Render with custom props:
<UserCard name="Anshuman" role="Architect" rating={5} />
Anshuman
Role: Architect
Rating: 5★2. Passing & Destructuring Props
In modern React, developers almost always destructure props directly inside the function parameter signature. This makes the code significantly cleaner and allows setting convenient default values.
1. Clean In-Line Destructuring
Extract specific keys right in the function arguments:
function Button({ label, variant = "primary", disabled = false }) {
return (
<button className={`btn btn-${variant}`} disabled={disabled}>
{label}
</button>
);
}
2. Props Spreading with Rest Operator (...)
Forward all remaining HTML attributes down to a native tag:
function CustomInput({ label, ...restProps }) {
return (
<div className="input-group">
<label>{label}</label>
<input {...restProps} />
</div>
);
}
// Usage: <CustomInput label="Age" type="number" min="18" />
3. Props Immutability (Pure Functions Rule)
A fundamental rule of React is that Props are strictly Read-Only and Immutable. A component must never modify its own props.
⚠️ Critical Rule: React Components Must Be Pure Functions with Respect to Props
// ❌ NEVER DO THIS: Direct prop mutation (Breaks React re-render tracking!)
function CartSummary(props) {
props.total = props.total + 10; // 💥 TypeError: Cannot assign to read-only property!
return <div>Total: ${props.total}</div>;
}
// ✅ CORRECT: Compute derived local values or use state
function CartSummary({ total, shippingCost = 10 }) {
const grandTotal = total + shippingCost; // Pure derived calculation
return <div>Grand Total: ${grandTotal}</div>;
}
4. Child-to-Parent Communication (Callback Functions)
Since data in React only flows downward (parent to child), how does a child notify a parent when a button is clicked or an input changes? The Parent passes a callback function as a prop down to the child!
Real-World Code Implementation
// 1. CHILD COMPONENT: Receives function via props and triggers it on user event
function SearchFilter({ onSearchChange }) {
return (
<input
type="text"
placeholder="Type to filter..."
onChange={(e) => onSearchChange(e.target.value)} // Sending data UP to parent
/>
);
}
// 2. PARENT COMPONENT: Holds state and passes handler down
function ProductCatalog() {
const [query, setQuery] = React.useState("");
function handleFilter(searchText) {
console.log("Parent received updated query:", searchText);
setQuery(searchText);
}
return (
<div className="catalog-wrapper">
<h2>Catalog Search</h2>
<SearchFilter onSearchChange={handleFilter} />
<p>Active Filter: <strong>{query}</strong></p>
</div>
);
}
Catalog Search
Active Filter: React Hooks
5. Top Interview Questions & Common Pitfalls
⚠️ Pitfall 1: "What is Prop Drilling and how do you solve it?"
The Interview Answer: Prop Drilling occurs when you pass props down through multiple layers of intermediate components that don't need the data themselves just to reach a deeply nested child component (e.g., App -> Dashboard -> Sidebar -> UserAvatar).
Solutions:
1. Component Composition: Pass the component as children.
2. React Context API: Broadcast data globally (e.g. Theme, Auth, Language).
3. State Management: Redux Toolkit, Zustand, or Jotai for large enterprise apps.
⚠️ Pitfall 2: "What is the difference between Props and State?"
- Props: Data passed into a component from its parent (External, Read-Only, Immutable).
- State: Data managed inside a component (Internal, Mutable via setter function, triggers re-render).
6. Hands-On Practice Challenges
Master component communication and props handling with these interactive exercises. Click "Solve Challenge" to open the code editor!
Challenge 1: Default Props & Destructuring Engine
Write a component simulator function buildUserBadge(props) that destructures username, role = "Member", isVerified = false, and points = 0. Calculate the tier (GOLD $\ge 100$, SILVER $\ge 50$, else BRONZE) and return the badge data.
- Destructure props with defaults for
role,isVerified, andpoints. - Calculate tier level based on points thresholds.
- Return displayName, verifiedTag, tierLevel, and badgeStyle object.
{ displayName: "anshuman_dev [Member]", verifiedTag: "VERIFIED_USER", tierLevel: "GOLD", badgeStyle: { borderColor: "#10B981", color: "#B45309" } }
buildUserBadge and test with various prop combinations.
Challenge 2: Simulate Child-to-Parent Callback Passing
Implement a closure createParentStore(initialCount) that returns an onIncrement(step) callback. Simulate a child button executing the callback with custom steps (e.g. $+5$, $+2$) and verify parent state synchronization.
- Encapsulate
countinsidecreateParentStore. - Provide
getCount()andonIncrement(step)methods. - Ensure executing
onIncrement(5)correctly updates parent state.
[Parent Callback Triggered] Count is now: 15 | Final Parent State: 17
getCount and onIncrement.
Challenge 3: Props Immutability Guard with Object.freeze
Write a function enforcePropsImmutability(propsObject) that returns a shallow-frozen copy of the props object using Object.freeze() to demonstrate how React protects components from accidental prop mutations.
- Clone the input object shallowly using
{ ...propsObject }. - Freeze it with
Object.freeze(...)and return it. - Verify with
Object.isFrozen(...).
Is Protected: true | Props Value: "dark"