AccueilClients

Applications et sites

  • Application métiersIntranet, back-office...
  • Applications mobilesAndroid & iOS
  • Sites InternetSites marketings et vitrines
  • Expertises techniques

  • React
  • Expo / React Native
  • Next.js
  • Node.js
  • Directus
  • TypeScript
  • Open SourceBlogContactEstimer

    1 octobre 2024

    Why Mutating State is Problematic in React

    3 minutes reading

    Why Mutating State is Problematic in React
    🇫🇷 This post is also available in french

    When I conduct React training sessions, one recurring issue participants face is state mutation. It's often a subtle problem, and to fully grasp it, you need to understand how React manages state.

    This article aims to clarify why mutating state is problematic in React and how to effectively avoid it.

    The Problem

    Let's consider a simple example. You have an array in the state that you want to update:

    const [items, setItems] = useState([1, 2, 3])
    
    const addItem = () => {
      items.push(4)
      setItems(items) // Oops, we're mutating the array here
    }
    

    When you run this function, you might expect the component to update with the added item. But no, nothing happens. Why? Because the reference of the items array hasn't changed. You only modified its content, and React doesn't detect that.

    The result: no UI update. What we have done here is a state mutation.

    Understanding Mutation

    Imagine your application as a residential neighborhood filled with houses, where each variable is a house with its own postal address. When you create a variable, you assign it an address.

    Array

    Mutating an object is like renovating the interior of a house without changing its address. You can repaint the walls, add furniture, but the address stays the same.

    Array

    React, on the other hand, doesn't look inside the houses. It only cares whether the address has changed. If the address remains the same, React assumes there is no change and doesn't trigger an update. This is where mutating state becomes problematic: you're altering the inside of the object, but React has no idea and continues to rely on the old version of the UI.

    Undesirable Effects

    The problem can also occur in hooks like useEffect, where mutating the state can prevent side effects from running. A classic example:

    user.username = 'Romy'
    
    useEffect(() => {
      console.log('user has changed!')
    }, [user])
    

    If you mutate the user object instead of creating a new reference, the effect will never be called. This bug doesn't generate visible errors but results in unexpected behaviors that can accumulate.

    The Solution: Copy, Don't Mutate

    So how do we avoid this problem? The solution is simple: instead of mutating your objects or arrays, create new copies using operators like the spread operator (...) or methods like concat. Here's how to fix our previous example:

    const addItem = () => {
      const newItems = [...items, 4] // New reference
      setItems(newItems) // State update
    }
    

    Here, we're creating a new array, which generates a new reference. React detects this change, and the UI updates correctly.


    Array

    Similarly, for objects:

    const updateUser = () => {
      const newUser = { ...user, age: 30 } // New reference
      setUser(newUser)
    }
    

    Note that for simple variables known as atomic (string, number, boolean...), we don't have this mutation problem.

    These data types are immutable by nature in JavaScript. When you "modify" a variable of this type, you're actually creating a new value with a new reference. This is why React can easily detect changes on these data types.

    Why This Matters

    React relies on reference comparison to optimize UI updates. Instead of performing a deep comparison (which would be performance-intensive), it simply checks if a new reference has been created. If it has, React knows it needs to reconcile the DOM with the new state.

    This optimization is crucial for maintaining acceptable performance, especially in complex applications. Comparing every property of an object or every element of an array on every state change would significantly slow down rendering. By creating a new reference, you ensure a smooth and predictable update process.

    In Summary

    Mutating state is like redecorating a house without changing the address: no one notices the changes. Creating a new reference, however, allows React to do its job and update the UI.

    So, next time you're managing state, remember: don't mutate, duplicate!

    👋🏼

    À découvrir également

    Premier Octet vous accompagne dans le développement de vos projets avec react

    Discuter de votre projet react