What is State?
State is data that changes over time. When state changes, React automatically re-renders your component. Think of it like a component's memory.
Props vs State:
- Props: Passed from parent, read-only, like function arguments
- State: Internal to component, mutable, like local variables
🎯 useState Hook
The most important React hook for managing state:
import { useState } from 'react';
function Counter() {
// Declare state variable
const [count, setCount] = useState(0);
// ↑ ↑ ↑
// value updater initial value
return (
<div>
<h2>Count: {count}</h2>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
How useState Works:
- Import:
import { useState } from 'react'; - Initialize:
const [value, setValue] = useState(initialValue); - Read: Use
valuedirectly - Update: Call
setValue(newValue) - Re-render: Component re-renders with new value
📊 Different Types of State
1. Number State:
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(count - 1)}>-1</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
2. String State:
function NameInput() {
const [name, setName] = useState('');
return (
<div>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter your name"
/>
<p>Hello, {name || 'Stranger'}!</p>
</div>
);
}
3. Boolean State:
function Toggle() {
const [isOn, setIsOn] = useState(false);
return (
<div>
<p>The light is {isOn ? 'ON' : 'OFF'}</p>
<button onClick={() => setIsOn(!isOn)}>
Toggle
</button>
</div>
);
}
4. Array State:
function TodoList() {
const [todos, setTodos] = useState(['Learn React', 'Build App']);
const [input, setInput] = useState('');
const addTodo = () => {
if (input.trim()) {
setTodos([...todos, input]); // Spread operator to add
setInput('');
}
};
const removeTodo = (index) => {
setTodos(todos.filter((_, i) => i !== index));
};
return (
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="New todo"
/>
<button onClick={addTodo}>Add</button>
<ul>
{todos.map((todo, index) => (
<li key={index}>
{todo}
<button onClick={() => removeTodo(index)}>×</button>
</li>
))}
</ul>
</div>
);
}
5. Object State:
function UserForm() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0
});
const handleChange = (field, value) => {
setUser({
...user, // Spread existing properties
[field]: value // Update specific field
});
};
return (
<div>
<input
value={user.name}
onChange={(e) => handleChange('name', e.target.value)}
placeholder="Name"
/>
<input
value={user.email}
onChange={(e) => handleChange('email', e.target.value)}
placeholder="Email"
/>
<input
type="number"
value={user.age}
onChange={(e) => handleChange('age', e.target.value)}
placeholder="Age"
/>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
<p>Age: {user.age}</p>
</div>
);
}
🖱️ Event Handling
Common Events:
function EventExamples() {
const [message, setMessage] = useState('');
// Click event
const handleClick = () => {
setMessage('Button clicked!');
};
// Change event (input)
const handleChange = (e) => {
setMessage(e.target.value);
};
// Submit event (form)
const handleSubmit = (e) => {
e.preventDefault(); // Prevent page reload
alert('Form submitted!');
};
// Mouse events
const handleMouseEnter = () => {
setMessage('Mouse entered!');
};
return (
<div>
{/* Click */}
<button onClick={handleClick}>Click Me</button>
{/* Change */}
<input onChange={handleChange} placeholder="Type here" />
{/* Submit */}
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
{/* Mouse events */}
<div
onMouseEnter={handleMouseEnter}
onMouseLeave={() => setMessage('Mouse left!')}
>
Hover over me
</div>
<p>{message}</p>
</div>
);
}
Event Object Properties:
function EventInfo() {
const handleEvent = (e) => {
console.log('Event type:', e.type);
console.log('Target element:', e.target);
console.log('Target value:', e.target.value);
console.log('Key pressed:', e.key); // For keyboard events
};
return (
<input
onChange={handleEvent}
onKeyDown={handleEvent}
/>
);
}
📝 Forms in React
Controlled Components:
React controls the form input values (recommended approach):
function LoginForm() {
const [formData, setFormData] = useState({
username: '',
password: ''
});
const [submitted, setSubmitted] = useState(false);
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('Login attempt:', formData);
setSubmitted(true);
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>Username:</label>
<input
type="text"
name="username"
value={formData.username}
onChange={handleChange}
required
/>
</div>
<div>
<label>Password:</label>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
required
/>
</div>
<button type="submit">>Login</button>
{submitted && (
<p>Welcome, {formData.username}!</p>
)}
</form>
);
}
Different Input Types:
function FormInputs() {
const [data, setData] = useState({
text: '',
email: '',
number: 0,
checkbox: false,
radio: '',
select: ''
});
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setData({
...data,
[name]: type === 'checkbox' ? checked : value
});
};
return (
<form>
{/* Text input */}
<input
type="text"
name="text"
value={data.text}
onChange={handleChange}
/>
{/* Email input */}
<input
type="email"
name="email"
value={data.email}
onChange={handleChange}
/>
{/* Number input */}
<input
type="number"
name="number"
value={data.number}
onChange={handleChange}
/>
{/* Checkbox */}
<label>
<input
type="checkbox"
name="checkbox"
checked={data.checkbox}
onChange={handleChange}
/>
Accept terms
</label>
{/* Radio buttons */}
<label>
<input
type="radio"
name="radio"
value="option1"
checked={data.radio === 'option1'}
onChange={handleChange}
/>
Option 1
</label>
{/* Select dropdown */}
<select name="select" value={data.select} onChange={handleChange}>
<option value="">Choose...</option>
<option value="a">Option A</option>
<option value="b">Option B</option>
</select>
</form>
);
}
✅ Form Validation
function SignupForm() {
const [formData, setFormData] = useState({
email: '',
password: '',
confirmPassword: ''
});
const [errors, setErrors] = useState({});
const validate = () => {
const newErrors = {};
// Email validation
if (!formData.email) {
newErrors.email = 'Email is required';
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
newErrors.email = 'Email is invalid';
}
// Password validation
if (!formData.password) {
newErrors.password = 'Password is required';
} else if (formData.password.length < 6) {
newErrors.password = 'Password must be at least 6 characters';
}
// Confirm password
if (formData.password !== formData.confirmPassword) {
newErrors.confirmPassword = 'Passwords do not match';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({ ...formData, [name]: value });
// Clear error when user types
if (errors[name]) {
setErrors({ ...errors, [name]: '' });
}
};
const handleSubmit = (e) => {
e.preventDefault();
if (validate()) {
alert('Form is valid!');
console.log('Submitting:', formData);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
{errors.email && <p className="error">{errors.email}</p>}
</div>
<div>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
placeholder="Password"
/>
{errors.password && <p className="error">{errors.password}</p>}
</div>
<div>
<input
type="password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={handleChange}
placeholder="Confirm Password"
/>
{errors.confirmPassword && <p className="error">{errors.confirmPassword}</p>}
</div>
<button type="submit">Sign Up</button>
</form>
);
}
🎯 Complete Example: Shopping Cart
function ShoppingCart() {
const [items, setItems] = useState([
{ id: 1, name: 'Laptop', price: 999, quantity: 1 },
{ id: 2, name: 'Mouse', price: 29, quantity: 2 }
]);
// Increase quantity
const increaseQuantity = (id) => {
setItems(items.map(item =>
item.id === id
? { ...item, quantity: item.quantity + 1 }
: item
));
};
// Decrease quantity
const decreaseQuantity = (id) => {
setItems(items.map(item =>
item.id === id && item.quantity > 1
? { ...item, quantity: item.quantity - 1 }
: item
));
};
// Remove item
const removeItem = (id) => {
setItems(items.filter(item => item.id !== id));
};
// Calculate total
const total = items.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
);
return (
<div className="cart">
<h2>Shopping Cart</h2>
{items.length === 0 ? (
<p>Your cart is empty</p>
) : (
<>
{items.map(item => (
<div key={item.id} className="cart-item">
<h3>{item.name}</h3>
<p>${item.price}</p>
<div className="quantity-controls">
<button onClick={() => decreaseQuantity(item.id)}>-</button>
<span>{item.quantity}</span>
<button onClick={() => increaseQuantity(item.id)}>+</button>
</div>
<p>Subtotal: ${item.price * item.quantity}</p>
<button onClick={() => removeItem(item.id)}>Remove</button>
</div>
))}
<div className="cart-total">
<h3>Total: ${total.toFixed(2)}</h3>
<button onClick={() => alert('Checkout!')}>
Checkout
</button>
</div>
</>
)}
</div>
);
}
⚠️ Common Mistakes
1. Modifying State Directly
// ❌ Wrong - mutates state
const [count, setCount] = useState(0);
count = count + 1; // Don't do this!
// ✅ Correct - use setter
setCount(count + 1);
2. Not Using Previous State
// ❌ Might miss updates
setCount(count + 1);
// ✅ Better - uses previous state
setCount(prevCount => prevCount + 1);
3. Forgetting e.preventDefault()
// ❌ Page will reload
const handleSubmit = (e) => {
console.log('Submitted');
};
// ✅ Prevents default behavior
const handleSubmit = (e) => {
e.preventDefault();
console.log('Submitted');
};
🎯 Key Takeaways
- useState: Hook for managing component state
- State triggers re-render: Component updates when state changes
- Never mutate state: Always use setter function
- Events: onClick, onChange, onSubmit, etc.
- Event object: Access with
(e)parameter - Controlled components: React manages form values
- e.preventDefault(): Stop default form submission
- Validation: Check data before submission