Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
State structure refers to how you organize and shape your component's state data. A well-designed state structure makes your components predictable, easier to debug, and simpler to maintain.
Keep related pieces of information together in logical groups
Don't store data that can be calculated from existing state
Avoid deeply nested structures when possible
Different scenarios call for different state structures. Let's explore the most common patterns and when to use each one.
Use individual primitive values for simple, independent state.
Best for: Simple forms, toggles, counters
Group related data into a single object when pieces change together.
Best for: User profiles, form data, configuration objects
Use nested structures for complex data with clear hierarchy.
Best for: Complex applications, multi-step forms, dashboard data
Follow these principles to create state structures that are easy to understand and maintain as your application grows.
See how organized state makes form management easier
function UserProfile() {
const [user, setUser] = React.useState({
personal: {
firstName: '',
lastName: '',
email: '',
phone: ''
},
preferences: {
newsletter: false,
notifications: true,
theme: 'light'
},
account: {
username: '',
bio: '',
website: ''
}
});
const updatePersonalInfo = (field, value) => {
setUser(prev => ({
...prev,
personal: {
...prev.personal,
[field]: value
}
}));
};
const updatePreferences = (field, value) => {
setUser(prev => ({
...prev,
preferences: {
...prev.preferences,
[field]: value
}
}));
};
const updateAccount = (field, value) => {
setUser(prev => ({
...prev,
account: {
...prev.account,
[field]: value
}
}));
};
const handleSubmit = (e) => {
e.preventDefault();
alert('Profile updated: ' + JSON.stringify(user, null, 2));
};
return (
<div className="container">
<h1>π€ User Profile</h1>
<form onSubmit={handleSubmit} className="profile-form">
<div className="form-section">
<h2>Personal Information</h2>
<div className="form-grid">
<input
type="text"
placeholder="First Name"
value={user.personal.firstName}
onChange={(e) => updatePersonalInfo('firstName', e.target.value)}
className="form-input"
/>
<input
type="text"
placeholder="Last Name"
value={user.personal.lastName}
onChange={(e) => updatePersonalInfo('lastName', e.target.value)}
className="form-input"
/>
<input
type="email"
placeholder="Email"
value={user.personal.email}
onChange={(e) => updatePersonalInfo('email', e.target.value)}
className="form-input"
/>
<input
type="tel"
placeholder="Phone"
value={user.personal.phone}
onChange={(e) => updatePersonalInfo('phone', e.target.value)}
className="form-input"
/>
</div>
</div>
<div className="form-section">
<h2>Preferences</h2>
<div className="checkbox-group">
<label className="checkbox-label">
<input
type="checkbox"
checked={user.preferences.newsletter}
onChange={(e) => updatePreferences('newsletter', e.target.checked)}
/>
Newsletter Subscription
</label>
<label className="checkbox-label">
<input
type="checkbox"
checked={user.preferences.notifications}
onChange={(e) => updatePreferences('notifications', e.target.checked)}
/>
Push Notifications
</label>
<select
value={user.preferences.theme}
onChange={(e) => updatePreferences('theme', e.target.value)}
className="form-select"
>
<option value="light">Light Theme</option>
<option value="dark">Dark Theme</option>
<option value="auto">Auto</option>
</select>
</div>
</div>
<div className="form-section">
<h2>Account Details</h2>
<div className="form-grid">
<input
type="text"
placeholder="Username"
value={user.account.username}
onChange={(e) => updateAccount('username', e.target.value)}
className="form-input"
/>
<input
type="url"
placeholder="Website"
value={user.account.website}
onChange={(e) => updateAccount('website', e.target.value)}
className="form-input"
/>
<textarea
placeholder="Bio"
value={user.account.bio}
onChange={(e) => updateAccount('bio', e.target.value)}
className="form-textarea"
rows={3}
/>
</div>
</div>
<button type="submit" className="submit-btn">
πΎ Save Profile
</button>
</form>
<div className="state-display">
<h3>π Current State Structure</h3>
<pre>{JSON.stringify(user, null, 2)}</pre>
</div>
</div>
);
}
const root = createRoot(document.getElementById('root'));
root.render(<UserProfile />);Loading preview...
Organized state for products, cart, and user interactions
function ShoppingCart() {
const [shopState, setShopState] = React.useState({
products: [
{ id: 1, name: 'Laptop', price: 999, category: 'Electronics', inStock: true },
{ id: 2, name: 'Book', price: 29, category: 'Education', inStock: true },
{ id: 3, name: 'Headphones', price: 199, category: 'Electronics', inStock: false },
{ id: 4, name: 'Coffee Mug', price: 15, category: 'Lifestyle', inStock: true }
],
cart: {
items: [],
total: 0,
itemCount: 0
},
filters: {
category: 'all',
inStockOnly: false,
searchTerm: ''
},
ui: {
showCart: false,
loading: false,
error: null
}
});
const addToCart = (product) => {
setShopState(prev => {
const existingItem = prev.cart.items.find(item => item.id === product.id);
let newItems;
if (existingItem) {
newItems = prev.cart.items.map(item =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 }
: item
);
} else {
newItems = [...prev.cart.items, { ...product, quantity: 1 }];
}
const newTotal = newItems.reduce((sum, item) => sum + (item.price * item.quantity), 0);
const newItemCount = newItems.reduce((sum, item) => sum + item.quantity, 0);
return {
...prev,
cart: {
items: newItems,
total: newTotal,
itemCount: newItemCount
}
};
});
};
const removeFromCart = (productId) => {
setShopState(prev => {
const newItems = prev.cart.items.filter(item => item.id !== productId);
const newTotal = newItems.reduce((sum, item) => sum + (item.price * item.quantity), 0);
const newItemCount = newItems.reduce((sum, item) => sum + item.quantity, 0);
return {
...prev,
cart: {
items: newItems,
total: newTotal,
itemCount: newItemCount
}
};
});
};
const updateFilter = (filterType, value) => {
setShopState(prev => ({
...prev,
filters: {
...prev.filters,
[filterType]: value
}
}));
};
const toggleCart = () => {
setShopState(prev => ({
...prev,
ui: {
...prev.ui,
showCart: !prev.ui.showCart
}
}));
};
const filteredProducts = shopState.products.filter(product => {
const matchesCategory = shopState.filters.category === 'all' || product.category === shopState.filters.category;
const matchesStock = !shopState.filters.inStockOnly || product.inStock;
const matchesSearch = product.name.toLowerCase().includes(shopState.filters.searchTerm.toLowerCase());
return matchesCategory && matchesStock && matchesSearch;
});
return (
<div className="container">
<h1>π Shopping Cart</h1>
<div className="shop-layout">
<div className="main-content">
<div className="filters-section">
<h2>Filters</h2>
<div className="filter-controls">
<input
type="text"
placeholder="Search products..."
value={shopState.filters.searchTerm}
onChange={(e) => updateFilter('searchTerm', e.target.value)}
className="search-input"
/>
<select
value={shopState.filters.category}
onChange={(e) => updateFilter('category', e.target.value)}
className="filter-select"
>
<option value="all">All Categories</option>
<option value="Electronics">Electronics</option>
<option value="Education">Education</option>
<option value="Lifestyle">Lifestyle</option>
</select>
<label className="checkbox-label">
<input
type="checkbox"
checked={shopState.filters.inStockOnly}
onChange={(e) => updateFilter('inStockOnly', e.target.checked)}
/>
In Stock Only
</label>
</div>
</div>
<div className="products-section">
<h2>Products</h2>
<div className="products-grid">
{filteredProducts.map(product => (
<div key={product.id} className="product-card">
<div className="product-info">
<h3>{product.name}</h3>
<p className="category">{product.category}</p>
<p className="price">${product.price}</p>
{!product.inStock && <span className="out-of-stock">Out of Stock</span>}
</div>
<button
onClick={() => addToCart(product)}
disabled={!product.inStock}
className="add-to-cart-btn"
>
{product.inStock ? 'Add to Cart' : 'Out of Stock'}
</button>
</div>
))}
</div>
</div>
</div>
<div className="cart-sidebar">
<div className="cart-header">
<h2>Cart ({shopState.cart.itemCount})</h2>
<button onClick={toggleCart} className="toggle-cart-btn">
{shopState.ui.showCart ? 'Hide' : 'Show'}
</button>
</div>
{shopState.ui.showCart && (
<div className="cart-content">
{shopState.cart.items.length === 0 ? (
<p className="empty-cart">Your cart is empty</p>
) : (
<>
<div className="cart-items">
{shopState.cart.items.map(item => (
<div key={item.id} className="cart-item">
<div className="item-info">
<h4>{item.name}</h4>
<p>${item.price} x {item.quantity}</p>
</div>
<button
onClick={() => removeFromCart(item.id)}
className="remove-btn"
>
Remove
</button>
</div>
))}
</div>
<div className="cart-summary">
<div className="total">
<strong>Total: ${shopState.cart.total}</strong>
</div>
<button className="checkout-btn">
Checkout
</button>
</div>
</>
)}
</div>
)}
</div>
</div>
<div className="state-display">
<h3>π Current State Structure</h3>
<pre>{JSON.stringify(shopState, null, 2)}</pre>
</div>
</div>
);
}
const root = createRoot(document.getElementById('root'));
root.render(<ShoppingCart />);Loading preview...
Begin with primitive values and only group data when you see a clear pattern or relationship.
Put data that updates together in the same object. This makes state updates more predictable.
Keep nesting to 2-3 levels maximum. Deep nesting makes updates complex and error-prone.
Calculate values on the fly instead of storing them in state when they depend on other state values.
Good state structure makes your components easier to understand, debug, and maintain.
Keep related pieces of information together in logical groups that update together.
Start simple and only add complexity when you have a clear need for it.
Your state structure should reflect how your UI is organized and what data is rendered together.