reactjsreact-strictmode

When I use a class method to update state of a model in React it fires twice in strict mode


Does that mean that I am doing something wrong?

I thought strict mode was to help you find side effects.

If I turn off strict mode I get the quantity updated correctly (once per click). With strict mode on it updates twice per click. In this example 1 it illustrates the twice per click behavior with strict mode on while using the Class method in the updateItem to update quantity:

class ItemModel {
  id: number
  quantity: number
  name: string

  constructor(name: string) {
    this.id = Math.random()
    this.quantity = 1
    this.name = name
  }

  increaseQuantity = () => {
    this.quantity = this.quantity + 1
    return this
  }
  decreaseQuantity = () => {
    this.quantity = this.quantity >= 2 ? this.quantity - 1 : 1
    return this
  }
}

....

  const [items, setItems] = useState<ItemProps[]>([])

....

  const updateItem = (id: number) => {
    setItems(items => items.map(item => (item.id === id ? { ...item.increaseQuantity() } : item)))
  }

....

<button type="button" onClick={() => updateItem(id)}>+ 1</button>

In example 2 of updateItem with strict mode on I get the correct behavior (update quantity once per click):

  const updateItem = (id: number) => {
    setItems(items => items.map(item => (item.id === id ? { ...item, quantity: item.quantity + 1 } : item)))
  }

In both examples, example 1 and example 2 with strict mode on, I am returning a new updated, non-mutated object to the array, so why I get different behavior when I use a class method to update the quantity in example 1 vs manually in example 2.


Solution

  • StrictMode has found an impure rerendering in example 1 and it's caused by this:

    ...item.increaseQuantity()
    

    What you think this does it creates a new object with an increased quantity.

    What this actually does is creates a new object with an increased quantity and mutates the existing object that is in the current state

    I am returning a new updated, non-mutated object to the array

    Nope, you've mutated item.

    Your ItemModel is a poor way to do react - use plain objects as much as possible.