Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
LoginSign Up
menu
  1. Home
  2. /Web Development
  3. /React
  4. /Multiple State Variables
Your Progress0%
0 of 42 completed

React Topics

Getting Started

  • What is React?
  • React vs Vanilla JavaScript
  • Setting Up Your Environment
  • Your First React App

JSX Fundamentals

  • Introduction to JSX
  • JSX Expressions and Variables
  • Conditional Rendering
  • Lists and Keys
  • Styling in JSX

Components Basics

  • Function Components
  • Props
  • Children Prop
  • Props Destructuring
  • Component Composition

State Management

  • Introduction to State
  • useState Hook
  • State Updates and Re-renders
  • Multiple State Variables
  • State Best Practices

Events and Interactivity

  • Handling Events
  • Event Objects
  • Forms in React
  • Form Validation

Side Effects and Data

  • Introduction to useEffect
  • Data Fetching
  • Cleanup Functions
  • Dependency Arrays

Advanced Hooks

  • useRef Hook
  • useContext Hook
  • useMemo Hook
  • useCallback Hook

React Patterns

  • Custom Hooks
  • Higher-Order Components
  • Render Props Pattern

React Router

  • Introduction to React Router
  • Routes and Navigation
  • Dynamic Routes and Parameters

Best Practices

  • Component Organization
  • Performance Optimization
  • Error Handling
  • Testing React Components

Real World Project

  • Building a Complete App

Multiple State Variables

Organizing state for clean, maintainable components

Real components rarely have just one piece of state. You might need to track a counter, form inputs, loading status, error messages, and more - all at the same time. Should you use one big object or multiple separate state variables? When should you split state and when should you combine it? In this lesson, you'll learn how to organize state effectively, making your components cleaner and easier to maintain.

Using Multiple useState Calls

You can call useState as many times as you need in a component:

multiple-state.jsx
function UserProfile() {
  // Multiple independent state variables
  const [name, setName] = React.useState('')
  const [age, setAge] = React.useState(0)
  const [email, setEmail] = React.useState('')
  const [isActive, setIsActive] = React.useState(false)
  const [notifications, setNotifications] = React.useState([])
  
  return (
    <div>
      <input 
        value={name} 
        onChange={e => setName(e.target.value)} 
        placeholder="Name"
      />
      <input 
        type="number"
        value={age} 
        onChange={e => setAge(Number(e.target.value))} 
        placeholder="Age"
      />
      <input 
        type="email"
        value={email} 
        onChange={e => setEmail(e.target.value)} 
        placeholder="Email"
      />
      <label>
        <input 
          type="checkbox"
          checked={isActive}
          onChange={e => setIsActive(e.target.checked)}
        />
        Active
      </label>
    </div>
  )
}

// Each state variable is independent
// Updating one doesn't affect the others

Benefits of Multiple State Variables:

  • Each piece of state has a clear, descriptive name
  • Easy to update individual values
  • No need to spread previous state
  • Clear what each setter function does

Using Single Object State

Alternatively, you can combine related data into a single state object:

object-state.jsx
function UserProfile() {
  // Single state object
  const [user, setUser] = React.useState({
    name: '',
    age: 0,
    email: '',
    isActive: false
  })
  
  const updateName = (e) => {
    setUser({ ...user, name: e.target.value })
  }
  
  const updateAge = (e) => {
    setUser({ ...user, age: Number(e.target.value) })
  }
  
  const updateEmail = (e) => {
    setUser({ ...user, email: e.target.value })
  }
  
  const toggleActive = () => {
    setUser({ ...user, isActive: !user.isActive })
  }
  
  return (
    <div>
      <input value={user.name} onChange={updateName} placeholder="Name" />
      <input type="number" value={user.age} onChange={updateAge} placeholder="Age" />
      <input type="email" value={user.email} onChange={updateEmail} placeholder="Email" />
      <label>
        <input 
          type="checkbox"
          checked={user.isActive}
          onChange={toggleActive}
        />
        Active
      </label>
    </div>
  )
}

// All related data in one object
// Need to spread previous state when updating

Benefits of Object State:

  • Related data grouped together
  • Easier to pass around as one unit
  • Matches API response structure
  • Fewer state variables to manage

When to Split State

✅ Split State When Values Are Independent

split-independent.jsx
// ✅ Good - these are independent
function Dashboard() {
  const [user, setUser] = React.useState(null)
  const [theme, setTheme] = React.useState('light')
  const [sidebarOpen, setSidebarOpen] = React.useState(false)
  const [notifications, setNotifications] = React.useState([])
  
  // User and theme are unrelated
  // Sidebar state doesn't depend on notifications
  // Each can change independently
}

✅ Split State for Different Update Patterns

split-patterns.jsx
// ✅ Good - different update frequencies
function SearchPage() {
  const [query, setQuery] = React.useState('')        // Changes on every keystroke
  const [results, setResults] = React.useState([])    // Changes after API call
  const [isLoading, setIsLoading] = React.useState(false)  // Changes before/after API
  const [error, setError] = React.useState(null)      // Changes on error
  
  // These update at different times and for different reasons
  // Keeping them separate makes logic clearer
}

✅ Split State for Primitive Values

split-primitives.jsx
// ✅ Good - simple values
function Counter() {
  const [count, setCount] = React.useState(0)
  const [step, setStep] = React.useState(1)
  const [isRunning, setIsRunning] = React.useState(false)
  
  // Each is a simple value that updates independently
  // No need to group them
}

When to Combine State

✅ Combine State When Values Are Related

combine-related.jsx
// ✅ Good - position coordinates belong together
function DraggableBox() {
  const [position, setPosition] = React.useState({ x: 0, y: 0 })
  
  const move = (dx, dy) => {
    setPosition(prev => ({
      x: prev.x + dx,
      y: prev.y + dy
    }))
  }
  
  // x and y are always used together
  // Makes sense as one object
}

// ❌ Bad - splitting related values
function DraggableBox() {
  const [x, setX] = React.useState(0)
  const [y, setY] = React.useState(0)
  
  const move = (dx, dy) => {
    setX(prev => prev + dx)  // Two separate updates
    setY(prev => prev + dy)  // Could get out of sync
  }
}

✅ Combine State for Form Data

combine-form.jsx
// ✅ Good - form fields grouped
function SignupForm() {
  const [formData, setFormData] = React.useState({
    username: '',
    email: '',
    password: '',
    confirmPassword: ''
  })
  
  const handleChange = (e) => {
    const { name, value } = e.target
    setFormData(prev => ({
      ...prev,
      [name]: value
    }))
  }
  
  // All form fields updated with one handler
  // Easy to validate, submit, or reset as a group
}

✅ Combine State That Updates Together

combine-together.jsx
// ✅ Good - these always update together
function DataFetcher() {
  const [apiState, setApiState] = React.useState({
    data: null,
    isLoading: false,
    error: null
  })
  
  const fetchData = async () => {
    setApiState({ data: null, isLoading: true, error: null })
    
    try {
      const response = await fetch('/api/data')
      const data = await response.json()
      setApiState({ data, isLoading: false, error: null })
    } catch (error) {
      setApiState({ data: null, isLoading: false, error })
    }
  }
  
  // These three values represent one logical state
  // Only one can be "active" at a time
}

State Organization Decision Framework

Ask These Questions:

  1. Do the values always update together?
    Yes → Combine into object
    No → Keep separate
  2. Are the values related conceptually?
    Yes (e.g., x/y coordinates, firstName/lastName) → Combine
    No (e.g., theme and user) → Keep separate
  3. Do you often pass them together?
    Yes → Combine into object
    No → Keep separate
  4. Is it a form with multiple fields?
    Yes → Usually combine
    No → Depends on other factors

General Rule of Thumb:

When in doubt, start with separate state variables. It's easier to combine them later if needed than to split a large object. Prefer simplicity and clarity over premature optimization.

Common State Organization Patterns

Pattern 1: Form State

pattern-form.jsx
function ContactForm() {
  // ✅ Combine form fields
  const [formData, setFormData] = React.useState({
    name: '',
    email: '',
    message: ''
  })
  
  // ✅ Keep validation separate
  const [errors, setErrors] = React.useState({})
  
  // ✅ Keep submission state separate
  const [isSubmitting, setIsSubmitting] = React.useState(false)
  
  const handleChange = (e) => {
    setFormData(prev => ({
      ...prev,
      [e.target.name]: e.target.value
    }))
  }
  
  const handleSubmit = async (e) => {
    e.preventDefault()
    setIsSubmitting(true)
    // Submit logic...
    setIsSubmitting(false)
  }
}

Pattern 2: Loading/Error State

pattern-loading.jsx
function UserProfile() {
  // ✅ Combine related loading state
  const [loadingState, setLoadingState] = React.useState({
    isLoading: false,
    error: null
  })
  
  // ✅ Keep actual data separate
  const [user, setUser] = React.useState(null)
  
  const fetchUser = async () => {
    setLoadingState({ isLoading: true, error: null })
    
    try {
      const response = await fetch('/api/user')
      const data = await response.json()
      setUser(data)
      setLoadingState({ isLoading: false, error: null })
    } catch (error) {
      setLoadingState({ isLoading: false, error: error.message })
    }
  }
}

Pattern 3: List with Filters

pattern-list.jsx
function ProductList() {
  // ✅ Keep source data separate
  const [products, setProducts] = React.useState([])
  
  // ✅ Group filter settings
  const [filters, setFilters] = React.useState({
    category: 'all',
    priceRange: [0, 1000],
    inStockOnly: false,
    sortBy: 'name'
  })
  
  // ✅ Keep search separate (updates frequently)
  const [searchQuery, setSearchQuery] = React.useState('')
  
  const updateFilter = (key, value) => {
    setFilters(prev => ({ ...prev, [key]: value }))
  }
}

Pattern 4: Wizard/Multi-Step Form

pattern-wizard.jsx
function SignupWizard() {
  // ✅ Keep step tracking separate
  const [currentStep, setCurrentStep] = React.useState(1)
  
  // ✅ Combine all form data
  const [formData, setFormData] = React.useState({
    // Step 1
    email: '',
    password: '',
    // Step 2
    firstName: '',
    lastName: '',
    // Step 3
    address: '',
    city: '',
    zipCode: ''
  })
  
  const nextStep = () => setCurrentStep(prev => prev + 1)
  const prevStep = () => setCurrentStep(prev => prev - 1)
  
  const updateFormData = (updates) => {
    setFormData(prev => ({ ...prev, ...updates }))
  }
}

Updating Nested State

When you have nested objects, updating them requires careful spreading:

nested-updates.jsx
function UserSettings() {
  const [settings, setSettings] = React.useState({
    profile: {
      name: 'Alice',
      avatar: '/avatar.jpg'
    },
    preferences: {
      theme: 'light',
      notifications: true
    }
  })
  
  // ❌ Wrong - only shallow copy
  const updateName = (newName) => {
    setSettings({
      ...settings,
      profile: { name: newName }  // This erases avatar!
    })
  }
  
  // ✅ Correct - deep copy
  const updateName = (newName) => {
    setSettings({
      ...settings,
      profile: {
        ...settings.profile,
        name: newName
      }
    })
  }
  
  // ✅ Better - helper function
  const updateProfile = (updates) => {
    setSettings(prev => ({
      ...prev,
      profile: {
        ...prev.profile,
        ...updates
      }
    }))
  }
  
  updateProfile({ name: 'Bob' })  // Easier!
}

Deep Nesting Warning:

If you have deeply nested state (3+ levels), consider:
1. Flattening your state structure
2. Using a state management library (Redux, Zustand)
3. Breaking into smaller components

Deep nesting makes updates error-prone and hard to read!

Interactive Demo

ReactPlayground.jsx
Code Editor
Preview

💡 Tip: Edit the code above and click "Run" to see your changes

State Organization Anti-Patterns

❌ Anti-Pattern 1: Duplicating State

anti-duplicate.jsx
// ❌ Bad - count and doubleCount are redundant
function Counter() {
  const [count, setCount] = React.useState(0)
  const [doubleCount, setDoubleCount] = React.useState(0)
  
  const increment = () => {
    setCount(count + 1)
    setDoubleCount((count + 1) * 2)  // Easy to forget!
  }
}

// ✅ Good - compute from existing state
function Counter() {
  const [count, setCount] = React.useState(0)
  const doubleCount = count * 2  // Calculated, not stored
  
  const increment = () => {
    setCount(count + 1)
  }
}

❌ Anti-Pattern 2: Mirroring Props in State

anti-mirror.jsx
// ❌ Bad - copying props to state
function UserDisplay({ user }) {
  const [userName, setUserName] = React.useState(user.name)
  
  // userName gets out of sync if user prop changes!
}

// ✅ Good - use props directly
function UserDisplay({ user }) {
  return <div>{user.name}</div>
}

// ✅ Or if you need to modify it locally
function UserDisplay({ user }) {
  const [editedName, setEditedName] = React.useState(user.name)
  
  // Make it clear this is a local edit, not the source
}

❌ Anti-Pattern 3: Too Much in One Object

anti-too-much.jsx
// ❌ Bad - everything in one giant object
function App() {
  const [state, setState] = React.useState({
    user: null,
    theme: 'light',
    sidebar: false,
    modal: false,
    notifications: [],
    cart: [],
    filters: {},
    searchQuery: '',
    // ... 20 more properties
  })
  
  // Updating is complex and error-prone
  const toggleSidebar = () => {
    setState({ ...state, sidebar: !state.sidebar })
  }
}

// ✅ Good - split into logical groups
function App() {
  const [user, setUser] = React.useState(null)
  const [theme, setTheme] = React.useState('light')
  const [uiState, setUiState] = React.useState({
    sidebar: false,
    modal: false
  })
  const [notifications, setNotifications] = React.useState([])
  const [cart, setCart] = React.useState([])
  
  const toggleSidebar = () => {
    setUiState(prev => ({ ...prev, sidebar: !prev.sidebar }))
  }
}

Key Takeaways

  • You can use multiple useState calls in one component
  • Split state when values are independent and update separately
  • Combine state when values are related and update together
  • Form data usually works well as a single object
  • Keep loading/error state separate from actual data
  • Use the spread operator to update nested state immutably
  • Avoid duplicating state - compute derived values instead
  • Don't mirror props in state unless you have a good reason
  • Start with separate state variables, combine if needed
  • Keep state flat - avoid deep nesting when possible
  • Group related state for easier updates and passing to children
  • State organization affects component maintainability

What's Next?

You now know how to organize state effectively! You understand when to split state into multiple variables and when to combine it into objects. Your components are cleaner and more maintainable.

But there are still some important best practices and patterns to learn. In the next lesson, you'll learn about state best practices:

  • Immutability and why it matters
  • Where to place state in your component tree
  • Avoiding common state mistakes
  • Advanced state patterns and techniques
  • When to lift state up or down

These best practices will help you write professional, production-ready React code! 🚀

Learning how to organize state effectively in React components!

Previous
State Updates and Re-renders
Next
State Best Practices

Never Miss a New React Tutorial

Join 2,000+ developers learning React step-by-step. Get new tutorials, tips, and exclusive resources delivered to your inbox - completely FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

React Tutorials

0 of 42 completed

Your Progress0%

Topics

Getting Started

  • What is React?
  • React vs Vanilla JavaScript
  • Setting Up Your Environment
  • Your First React App

JSX Fundamentals

  • Introduction to JSX
  • JSX Expressions and Variables
  • Conditional Rendering
  • Lists and Keys
  • Styling in JSX

Components Basics

  • Function Components
  • Props
  • Children Prop
  • Props Destructuring
  • Component Composition

State Management

  • Introduction to State
  • useState Hook
  • State Updates and Re-renders
  • Multiple State Variables
  • State Best Practices

Events and Interactivity

  • Handling Events
  • Event Objects
  • Forms in React
  • Form Validation

Side Effects and Data

  • Introduction to useEffect
  • Data Fetching
  • Cleanup Functions
  • Dependency Arrays

Advanced Hooks

  • useRef Hook
  • useContext Hook
  • useMemo Hook
  • useCallback Hook

React Patterns

  • Custom Hooks
  • Higher-Order Components
  • Render Props Pattern

React Router

  • Introduction to React Router
  • Routes and Navigation
  • Dynamic Routes and Parameters

Best Practices

  • Component Organization
  • Performance Optimization
  • Error Handling
  • Testing React Components

Real World Project

  • Building a Complete App
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo