Props and State
Master React's data flow with props and state management
90 min•By Priygop Team•Last updated: Feb 2026
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.
Props Examples
Example
// 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
Example
// 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
- Props trigger re-renders when parent updates them
- State triggers re-renders when setState is called
- Props are used for configuration and data flow
- State is used for component-specific data
Advanced Props Patterns
Example
// 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
Example
// 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
Example
// 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 codesTry It Yourself — Props & State Demo
Try It Yourself — Props & State DemoHTML
HTML Editor
✓ ValidTab = 2 spaces
HTML|77 lines|3766 chars|✓ Valid syntax
UTF-8