Structuring State in React: 5 Essential Patterns

Structuring State in React: 5 Essential Patterns

Learn 5 essential patterns for structuring React state effectively. Discover how to group related data, avoid contradictions, eliminate redundancy, and keep your components maintainable and bug-free.

Aurora Scharff

Aurora Scharff

July 23, 2025

How you organize state in your React components can make the difference between code that's maintainable and code that becomes a debugging nightmare. This guide explores proven patterns for structuring state effectively, helping you build components that remain predictable and easy to work with as they grow in complexity.

When multiple pieces of state are naturally connected, keeping them separate creates opportunities for synchronization bugs:

// Problematic: separate state for related data
const [productId, setProductId] = useState(null);
const [quantity, setQuantity] = useState(1);
const [price, setPrice] = useState(0);

Instead, group related data together:

// Better: grouped related state
const [cartItem, setCartItem] = useState({
  productId: null,
  quantity: 1,
  price: 0
});

This pattern works particularly well for user profiles, API responses, and any data that represents a cohesive entity.

2. Avoid Contradictions

State combinations that can create impossible scenarios lead to unpredictable UI behavior:

// Dangerous: contradictory states possible
const [isLoading, setIsLoading] = useState(false);
const [hasError, setHasError] = useState(false);

What should the UI show if both are true? Use a single state variable for mutually exclusive states:

// Clear: only valid states possible
const [status, setStatus] = useState('idle');
// 'idle' | 'loading' | 'success' | 'error'

This finite state machine approach eliminates impossible combinations and makes component behavior predictable.

3. Avoid Redundant State

Storing computed values as separate state creates synchronization headaches:

// Problematic: redundant computed state
const [items, setItems] = useState([]);
const [totalPrice, setTotalPrice] = useState(0);

Instead, compute derived values during render:

// Better: compute derived values
const [items, setItems] = useState([]);
const totalPrice = items.reduce((sum, item) => sum + item.price, 0);

This ensures derived values are always in sync with their source data.

4. Avoid Duplication

Storing the same data in multiple places creates consistency problems:

// Problematic: duplicated data
const [todos, setTodos] = useState([
  { id: 1, text: 'Learn React', completed: false }
]);
const [editingTodo, setEditingTodo] = useState({
  id: 1, text: 'Learn React', completed: false
});

Store references instead:

// Better: single source of truth
const [todos, setTodos] = useState([
  { id: 1, text: 'Learn React', completed: false }
]);
const [editingId, setEditingId] = useState(null);

const editingTodo = todos.find(todo => todo.id === editingId);

This database-like approach with IDs as references prevents synchronization issues.

5. Avoid Deep Nesting

Deeply nested state makes updates complex and error-prone:

// Difficult to update
const [user, setUser] = useState({
  profile: {
    contact: {
      email: 'john@example.com'
    }
  }
})

Flatten your state structure when possible:

// Simpler flat structure
const [userEmail, setUserEmail] = useState('john@example.com');

// Or use focused state objects
const [userContact, setUserContact] = useState({
  email: 'john@example.com',
  phone: '123-456-7890'
});

Conclusion

These patterns solve specific problems you'll encounter:

  • Group related state when data changes together (form fields, coordinates, API responses)
  • Use status enums for mutually exclusive states (loading states, modal visibility, user roles)
  • Compute derived values instead of storing them (totals, filtered lists, formatted data)
  • Store references when the same entity appears in multiple contexts (selected items, active users)
  • Flatten structures when nesting makes updates complex (deeply nested forms, configuration objects)

The key is recognizing these patterns in your own code and applying the appropriate solution. Good state structure isn't about following rigid rules—it's about choosing the right approach for each specific situation to keep your components maintainable and bug-free.


Sources:

More certificates.dev articles

Get the latest news and updates on developer certifications. Content is updated regularly, so please make sure to bookmark this page or sign up to get the latest content directly in your inbox.

Looking for Certified Developers?

We can help you recruit Certified Developers for your organization or project. The team has helped many customers employ suitable resources from a pool of 100s of qualified Developers.

Let us help you get the resources you need.

Contact Us
Customer Testimonial for Hiring
like a breath of fresh air
Everett Owyoung
Everett Owyoung
Head of Talent for ThousandEyes
(a Cisco company)