React Props & State
Pass data with props, manage change with state, and flow data between components
Props
useState
Lifting State
Data Flow
1. Introduction: Understanding Data in React
Every React application needs to manage data. React provides two main ways to handle data:
Concept Purpose Analogy
Props Pass data from parent to child (read-only) A gift with a "do not open until Christmas" label
State Data that changes over time within a component A whiteboard you can write on and erase
Golden Rule: Props are passed down. State is managed within.
2. What are Props?
Props (short for "properties") are read-only inputs passed from a parent component to a child component.
2.1 Passing Props to Components
JSX function Parent() {
return (
<Child
name="Alice"
age={25}
isLoggedIn={true}
colors={["red", "blue"]}
user={{ id: 1, role: "admin" }}
/>
);
}
2.2 Accessing Props in Functional Components
JSX function Child(props) {
return <h1>Hello, {props.name}!</h1>;
}
function Child({ name, age, isLoggedIn }) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>Age: {age}</p>
<p>Status: {isLoggedIn ? "Logged In" : "Logged Out"}</p>
</div>
);
}
2.3 Accessing Props in Class Components
JSX class Child extends React.Component {
render() {
return (
<div>
<h1>Hello, {this.props.name}!</h1>
<p>Age: {this.props.age}</p>
</div>
);
}
}
2.4 Props are Read-Only (Immutability)
You cannot modify props inside a child component.
JSX // WRONG
function Child({ name }) {
name = "Bob"; // Cannot reassign props
return <h1>{name}</h1>;
}
// CORRECT
function Child({ name }) {
return <h1>{name}</h1>;
}
2.5 Default Props and PropTypes
JSX import PropTypes from 'prop-types';
function Greeting({ name, age }) {
return <h1>{name} is {age} years old</h1>;
}
Greeting.defaultProps = { name: "Guest", age: 18 };
Greeting.propTypes = {
name: PropTypes.string,
age: PropTypes.number.isRequired
};
<Greeting age={25} /> // name defaults to "Guest"
2.6 Destructuring Props
JSX function User({ user, role }) {
const { name, email } = user;
return (
<div>
<p>Name: {name}</p>
<p>Email: {email}</p>
<p>Role: {role}</p>
</div>
);
}
2.7 children Prop (Nested Content)
JSX function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-content">{children}</div>
</div>
);
}
<Card title="Welcome">
<p>This content is passed as children prop</p>
<button>Click Me</button>
</Card>
3. What is State?
State is data that is private to a component and can change over time. When state changes, React re-renders the component.
3.1 State vs Props: The Key Difference
Aspect Props State
Ownership Owned by parent Owned by the component itself
Mutability Immutable Mutable (via setter)
Purpose Pass data parent → child Track changing internal data
Who can change? Only the parent The component itself
Trigger re-render? Yes, when props change Yes, when state changes
JSX // Props — data flows DOWN
<Child message="Hello" />
// State — data is INTERNAL
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
3.2 State in Functional Components (useState Hook)
JSX import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const [name, setName] = useState("");
const [isActive, setIsActive] = useState(false);
const [user, setUser] = useState({ id: 1, name: "John" });
const [items, setItems] = useState([]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
Multiple useState calls for separate pieces of state (email, password, errors in a form).
3.3 State in Class Components (this.state)
JSX class Counter extends React.Component {
state = { count: 0, name: "", user: { id: 1, name: "John" } };
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
3.4 When to Use State vs Props
Use State When... Use Props When...
Data changes over time (input, API) Data is static or from parent
Data is private to the component Data shared with child components
Track internal component data Reusable component with different configs
JSX // Good: input uses state
function SearchBox() {
const [query, setQuery] = useState("");
return <input value={query} onChange={(e) => setQuery(e.target.value)} />;
}
// Good: display uses props
function DisplayName({ name }) {
return <h1>{name}</h1>;
}
4. State Updates in React
4.1 Never Mutate State Directly
JSX // WRONG
count = count + 1; // React won't re-render
// CORRECT
setCount(count + 1);
4.2 setState is Asynchronous
JSX setCount(count + 1);
console.log(count); // Still the OLD value!
useEffect(() => {
console.log("Count updated to:", count);
}, [count]);
4.3 Functional Updates (When New State Depends on Previous State)
JSX // PROBLEM: both use stale count — only +1
setCount(count + 1);
setCount(count + 1);
// SOLUTION: functional update — +2
setCount(prev => prev + 1);
setCount(prev => prev + 1);
Use functional updates when: multiple updates in one function, new state depends on previous, or inside useEffect with empty deps.
4.4 Object State Updates (Merging and Spreading)
JSX // WRONG
user.age = 26;
setUser(user);
// CORRECT
setUser({ ...user, age: 26 });
setUser(prev => ({ ...prev, age: prev.age + 1, isActive: true }));
// Nested update
setState(prev => ({
...prev,
user: {
...prev.user,
address: { ...prev.user.address, city: "Delhi" }
}
}));
4.5 Array State Updates (Adding, Removing, Updating Items)
JSX // ADD
setItems([...items, newItem]);
setItems([newItem, ...items]);
// REMOVE
setItems(items.filter(item => item.id !== idToRemove));
// UPDATE
setItems(items.map(item =>
item.id === idToUpdate ? { ...item, completed: true } : item
));
// WRONG — direct mutation
items.push(newItem);
setItems(items);
4.6 Batching of State Updates
React batches state updates in the same synchronous event handler — only one re-render at the end. In React 18+, updates in promises and timeouts are also batched.
JSX const handleSubmit = () => {
setName("John");
setAge(25);
setEmail("john@example.com");
// Only 1 re-render
};
5. Passing Data Between Components
5.1 Parent to Child (via Props) – One-Way Data Flow
JSX function App() {
const [user, setUser] = useState({ name: "Alice", role: "Admin" });
return (
<div>
<Profile user={user} />
<Dashboard role={user.role} />
</div>
);
}
function Profile({ user }) {
return <h1>Welcome, {user.name}!</h1>;
}
5.2 Child to Parent (via Callback Functions)
JSX function Parent() {
const [dataFromChild, setDataFromChild] = useState("");
const handleChildData = (childData) => setDataFromChild(childData);
return (
<div>
<p>Data from child: {dataFromChild}</p>
<Child onSendData={handleChildData} />
</div>
);
}
function Child({ onSendData }) {
return <button onClick={() => onSendData("Hello from Child!")}>Send</button>;
}
5.3 Sibling to Sibling (Lifting State Up)
Lift state to the closest common ancestor when siblings need to share data.
JSX function App() {
const [selectedItem, setSelectedItem] = useState(null);
return (
<div>
<ItemList onSelectItem={setSelectedItem} />
<ItemDetails item={selectedItem} />
</div>
);
}
5.4 Distant Components (Context API) – Overview
JSX const UserContext = React.createContext();
function App() {
const [user, setUser] = useState({ name: "Alice", role: "Admin" });
return (
<UserContext.Provider value={{ user, setUser }}>
<DeeplyNestedComponent />
</UserContext.Provider>
);
}
function DeeplyNestedComponent() {
const { user } = useContext(UserContext);
return <h1>Hello, {user.name}</h1>;
}
5.5 Component Composition (Alternative to Prop Drilling)
JSX // GOOD: pass components, not props through levels
function App() {
const [user, setUser] = useState({ name: "Alice" });
return (
<Layout
header={<UserHeader user={user} />}
content={<UserContent user={user} />}
/>
);
}
function Layout({ header, content }) {
return <div><div className="header">{header}</div><div>{content}</div></div>;
}
6. Complete Working Examples
6.1 Example 1: Todo App (Props + State + Lifting State Up)
JSX function App() {
const [todos, setTodos] = useState([]);
const addTodo = (text) => {
setTodos([...todos, { id: Date.now(), text, completed: false }]);
};
const toggleTodo = (id) => {
setTodos(todos.map(t => t.id === id ? { ...t, completed: !t.completed } : t));
};
const deleteTodo = (id) => setTodos(todos.filter(t => t.id !== id));
return (
<div>
<h1>My Todo App</h1>
<TodoForm onAdd={addTodo} />
<TodoList todos={todos} onToggle={toggleTodo} onDelete={deleteTodo} />
</div>
);
}
function TodoForm({ onAdd }) {
const [input, setInput] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
if (input.trim()) { onAdd(input); setInput(""); }
};
return (
<form onSubmit={handleSubmit}>
<input value={input} onChange={(e) => setInput(e.target.value)} />
<button type="submit">Add</button>
</form>
);
}
function TodoItem({ todo, onToggle, onDelete }) {
return (
<li style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
<input type="checkbox" checked={todo.completed} onChange={() => onToggle(todo.id)} />
{todo.text}
<button onClick={() => onDelete(todo.id)}>Delete</button>
</li>
);
}
6.2 Example 2: User Profile Dashboard
JSX function App() {
const [user, setUser] = useState({
name: "John Doe", email: "john@example.com", age: 28, isActive: true
});
const updateUser = (updates) => setUser(prev => ({ ...prev, ...updates }));
return (
<div>
<ProfileCard user={user} />
<EditForm user={user} onUpdate={updateUser} />
<ActivityStatus isActive={user.isActive} />
</div>
);
}
function EditForm({ user, onUpdate }) {
const [formData, setFormData] = useState(user);
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({ ...prev, [name]: type === 'checkbox' ? checked : value }));
};
const handleSubmit = (e) => { e.preventDefault(); onUpdate(formData); };
return (
<form onSubmit={handleSubmit}>
<input name="name" value={formData.name} onChange={handleChange} />
<input name="email" value={formData.email} onChange={handleChange} />
<button type="submit">Save</button>
</form>
);
}
7. Common Mistakes and Best Practices
Mistake Why It's Wrong Correct Way
Mutating state directly React won't re-render Use setter with new object/array
Calling setState in a loop Multiple re-renders Collect updates, apply once
Using state right after setState setState is async Use useEffect or functional updates
Not using functional updates Stale closure bugs setCount(prev => prev + 1)
State for derived data Wastes memory Compute: count % 2 === 0
One giant state object Hard to update Split into multiple useState
JSX // DO
const [count, setCount] = useState(0);
setCount(prev => prev + 1);
setUser({ ...user, age: user.age + 1 });
const completedCount = todos.filter(t => t.completed).length;
// DON'T
state.count = 5;
setState(state);
8. Summary Cheat Sheet
JSX // PROPS
<Child name="Alice" age={25} />
function Child({ name, age }) { return <div>{name}</div>; }
function Card({ children }) { return <div>{children}</div>; }
// STATE
const [value, setValue] = useState(initialValue);
setValue(newValue);
setValue(prev => prev + 1);
// Object / Array
setUser({ ...user, name: "John" });
setItems([...items, newItem]);
setItems(items.filter(i => i.id !== id));
// DATA FLOW
// Parent → Child: props
// Child → Parent: callback prop
// Siblings: lift state to parent
Next Steps
Event Handling – onClick, onChange, onSubmit
Forms in React – Controlled vs uncontrolled components
useEffect Deep Dive – Side effects, API calls
Context API – Avoid prop drilling
useReducer Hook – Complex state logic
React Props & State MCQ Practice
10 Basic MCQs
10 Advanced MCQs
10 Basic React Props & State MCQs
1
Props in React are:
A Read-only data from parent to child
B Mutable local variables
C Global CSS classes
D Router paths only
Explanation: Children should not modify props directly.
2
State is used when:
A Data changes inside a component over time
B Passing data from child to parent only
C Styling with Tailwind only
D Installing npm packages
Explanation: State triggers re-renders when updated.
3
useState returns:
A Current value and updater function
B Only a DOM node
C A Promise
D props object
Explanation: Destructuring: const [x, setX] = useState(0).
4
Updating state correctly:
A Call the setter function setCount(1)
B Mutate count = 1 directly
C Edit props.title
D Change DOM innerHTML only
Explanation: Direct mutation does not trigger re-renders.
5
Props flow direction is:
A Parent to child
B Child to parent only
C Random
D From CSS to JSX
Explanation: One-way data flow down the tree.
6
Default props can be set with:
A Default parameters or defaultProps
B Deleting state
C useEffect only
D localStorage only
Explanation: Function params: function Button({ label = 'Click' }).
7
Lifting state up means:
A Move shared state to common ancestor
B Delete all state
C Use only refs
D Store in window
Explanation: Siblings share data via parent props.
8
Props are accessed in a function component via:
A Function parameters
B this.props only
C document.getElementById
D import.meta
Explanation: function Greeting({ name }).
9
Calling setState with same primitive value:
A May bail out of re-render in React 18+
B Always reloads page
C Throws error
D Updates props
Explanation: React compares with Object.is for primitives.
10
Immutable state update for objects means:
A Spread previous state: setUser({...user, name})
B user.name = 'x' only
C Delete user
D Use innerHTML
Explanation: Create new object/array references.
10 Advanced React Props & State MCQs
1
Stale closure in useState often happens when:
A Event handler captures old state without functional update
B Using too many components
C JSX is invalid
D npm is outdated
Explanation: Use setCount(c => c + 1) when next state depends on previous.
2
Batching in React 18 means:
A Multiple setStates in same event may cause one re-render
B Each setState always reloads page
C State updates are synchronous in DOM always
D Props become mutable
Explanation: Automatic batching improves performance.
3
Prop drilling refers to:
A Passing props through many intermediate layers
B Using Context always
C Server-side rendering
D CSS modules
Explanation: Context or composition can reduce drilling.
4
Derived state anti-pattern is:
A Copying props into state without need
B Using useMemo for calculations
C Lifting state up
D Using keys in lists
Explanation: Prefer computing from props during render when possible.
5
Shallow merge with setState for objects in useState:
A Replace entire object unless you spread previous
B Deep merge automatically
C Mutate nested fields safely
D Props update automatically
Explanation: useState does not deep-merge objects.
6
Children as function pattern (render props) passes:
A A function child that receives data from parent
B Only CSS classes
C Router config
D Redux store
Explanation: Parent calls children(data) to share logic.
7
key prop on component at same level helps:
A Preserve identity when reordering lists
B Style buttons
C Enable npm
D Replace useState
Explanation: Keys affect reconciliation of component state.
8
Controlled vs state: input value tied to state is:
A Controlled component
B Uncontrolled
C Server component
D Static HTML only
Explanation: value + onChange sync with React state.
9
useReducer is preferable when:
A State logic is complex with multiple sub-values
B No state needed
C Only CSS changes
D Installing packages
Explanation: Reducer centralizes transition logic.
10
Passing callbacks via props allows child to:
A Notify parent without mutating parent props
B Mutate parent state directly
C Skip rendering
D Access database
Explanation: onSave={handleSave} is common pattern.
Check All Answers
15 React Props & State Interview Questions & Answers
Easy
Medium
Hard
1 What is the difference between props and state?easy
Answer: Props are read-only inputs from parent; state is internal mutable data managed by the component.
2 Can you modify props inside a child?easy
Answer: No—props are immutable; request changes via callback props to parent.
3 How do you update state from an event handler?easy
Answer: Call the setter from useState, e.g. setCount(count + 1).
4 What is lifting state up?medium
Answer: Moving shared state to the closest common ancestor and passing it down as props.
5 Why avoid mutating state directly?medium
Answer: React compares references; mutation skips re-render and breaks predictability.
6 What is prop drilling?medium
Answer: Passing data through many layers of components that do not need the data.
7 When use functional setState?medium
Answer: When new state depends on previous state to avoid stale closures.
8 What are default props?easy
Answer: Fallback values when parent does not pass a prop.
9 Can state hold objects and arrays?medium
Answer: Yes—always replace with new references when updating nested data.
10 How does parent receive data from child?medium
Answer: Pass a callback prop like onChange={handleChange} for child to invoke.
11 What is one-way data flow?medium
Answer: Data flows down via props; events flow up via callbacks.
12 useState vs useReducer?hard
Answer: useState for simple values; useReducer for complex state transitions.
13 What causes stale state in hooks?hard
Answer: Closures capturing old state—fix with functional updates or correct deps.
14 Are props available in useEffect dependency array?medium
Answer: Yes—when effects should re-run if a prop changes.
15 Explain batching.hard
Answer: React groups multiple state updates into one render for performance.