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.
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.