Props and State
Master React's data flow with props and state management. This is a foundational concept in component-based UI development that professional developers rely on daily. The explanations below are written to be beginner-friendly while covering the depth and nuance that comes from real-world React experience. Take your time with each section and practice the examples
What are Props?
Props (properties) are a way to pass data from parent to child components. They are read-only and help make components reusable and configurable.. This is an essential concept that every React developer must understand thoroughly. In professional development environments, getting this right can mean the difference between code that works reliably and code that breaks in production. The following sections break this down into clear, digestible pieces with practical examples you can try immediately
Props Examples
// Passing props to component
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}
// Using the component
<Welcome name="John" />
// Destructuring props
function Welcome({ name, age, city }) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>Age: {age}</p>
<p>City: {city}</p>
</div>
);
}
// Using with destructured props
<Welcome name="John" age={25} city="New York" />
// Props with children
function Container({ children }) {
return <div className="container">{children}</div>;
}
<Container>
<h1>This is a child</h1>
<p>This is another child</p>
</Container>State Management
// Using useState hook (Function Component)
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<button onClick={() => setCount(count - 1)}>
Decrement
</button>
</div>
);
}
// Class component with state
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
increment = () => {
this.setState({ count: this.state.count + 1 });
}
decrement = () => {
this.setState({ count: this.state.count - 1 });
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
<button onClick={this.decrement}>Decrement</button>
</div>
);
}
}Props vs State
- Props are read-only and passed from parent to child
- State is mutable and managed within the component — a critical concept in component-based UI development that you will use frequently in real projects
- Props trigger re-renders when parent updates them — a critical concept in component-based UI development that you will use frequently in real projects
- State triggers re-renders when setState is called — a critical concept in component-based UI development that you will use frequently in real projects
- Props are used for configuration and data flow — a critical concept in component-based UI development that you will use frequently in real projects
- State is used for component-specific data — a critical concept in component-based UI development that you will use frequently in real projects
Advanced Props Patterns
// Props with default values
function Button({
children,
variant = 'primary',
size = 'medium',
disabled = false,
onClick
}) {
const className = `btn btn-${variant} btn-${size}`;
return (
<button
className={className}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
}
// Props validation with PropTypes
import PropTypes from 'prop-types';
function UserCard({ user, onEdit, onDelete }) {
return (
<div className="user-card">
<h3>{user.name}</h3>
<p>{user.email}</p>
<div className="actions">
<button onClick={() => onEdit(user.id)}>Edit</button>
<button onClick={() => onDelete(user.id)}>Delete</button>
</div>
</div>
);
}
UserCard.propTypes = {
user: PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired
}).isRequired,
onEdit: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired
};
// Props spreading
function FormField({ label, ...inputProps }) {
return (
<div className="form-field">
<label>{label}</label>
<input {...inputProps} />
</div>
);
}
// Usage
<FormField
label="Email"
type="email"
placeholder="Enter your email"
required
/>Advanced State Management
// Complex state with objects
function UserProfile() {
const [user, setUser] = useState({
name: '',
email: '',
preferences: {
theme: 'light',
notifications: true
}
});
const updateUser = (field, value) => {
setUser(prev => ({
...prev,
[field]: value
}));
};
const updatePreference = (key, value) => {
setUser(prev => ({
...prev,
preferences: {
...prev.preferences,
[key]: value
}
}));
};
return (
<div>
<input
value={user.name}
onChange={(e) => updateUser('name', e.target.value)}
placeholder="Name"
/>
<input
value={user.email}
onChange={(e) => updateUser('email', e.target.value)}
placeholder="Email"
/>
<select
value={user.preferences.theme}
onChange={(e) => updatePreference('theme', e.target.value)}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
);
}
// State with arrays
function TodoList() {
const [todos, setTodos] = useState([]);
const [newTodo, setNewTodo] = useState('');
const addTodo = () => {
if (newTodo.trim()) {
setTodos(prev => [...prev, {
id: Date.now(),
text: newTodo,
completed: false
}]);
setNewTodo('');
}
};
const toggleTodo = (id) => {
setTodos(prev => prev.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
));
};
const deleteTodo = (id) => {
setTodos(prev => prev.filter(todo => todo.id !== id));
};
return (
<div>
<input
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="Add new todo"
/>
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{
textDecoration: todo.completed ? 'line-through' : 'none'
}}>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}Practice Exercise: State Management
// Exercise: Build a Shopping Cart
// Create a shopping cart with state management
// src/components/ShoppingCart.js
import React, { useState } from 'react';
function ShoppingCart() {
const [cart, setCart] = useState([]);
const [products] = useState([
{ id: 1, name: 'Laptop', price: 999, stock: 5 },
{ id: 2, name: 'Mouse', price: 25, stock: 10 },
{ id: 3, name: 'Keyboard', price: 75, stock: 8 }
]);
const addToCart = (product) => {
setCart(prev => {
const existingItem = prev.find(item => item.id === product.id);
if (existingItem) {
return prev.map(item =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 }
: item
);
} else {
return [...prev, { ...product, quantity: 1 }];
}
});
};
const removeFromCart = (productId) => {
setCart(prev => prev.filter(item => item.id !== productId));
};
const updateQuantity = (productId, newQuantity) => {
if (newQuantity <= 0) {
removeFromCart(productId);
} else {
setCart(prev => prev.map(item =>
item.id === productId
? { ...item, quantity: newQuantity }
: item
));
}
};
const getTotalPrice = () => {
return cart.reduce((total, item) => total + (item.price * item.quantity), 0);
};
return (
<div className="shopping-cart">
<div className="products">
<h2>Products</h2>
{products.map(product => (
<div key={product.id} className="product">
<h3>{product.name}</h3>
<p>{product.price}</p>
<p>Stock: {product.stock}</p>
<button onClick={() => addToCart(product)}>
Add to Cart
</button>
</div>
))}
</div>
<div className="cart">
<h2>Shopping Cart</h2>
{cart.length === 0 ? (
<p>Your cart is empty</p>
) : (
<>
{cart.map(item => (
<div key={item.id} className="cart-item">
<span>{item.name}</span>
<span>{item.price}</span>
<input
type="number"
value={item.quantity}
onChange={(e) => updateQuantity(item.id, parseInt(e.target.value))}
min="1"
/>
<button onClick={() => removeFromCart(item.id)}>
Remove
</button>
</div>
))}
<div className="cart-total">
<strong>Total: {getTotalPrice()}</strong>
</div>
<button className="checkout-btn">Checkout</button>
</>
)}
</div>
</div>
);
}
// Challenge: Add stock validation
// Challenge: Add a wishlist feature
// Challenge: Add discount codes