React docs:
Never mutate this.state directly, as calling setState() afterwards may
replace the mutation you made. Treat this.state as if it were
immutable.
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState
and calls may be batched for performance gains.
setState() will always trigger a re-render unless conditional
rendering logic is implemented in shouldComponentUpdate(). If mutable
objects are being used and the logic cannot be implemented in
shouldComponentUpdate(), calling setState() only when the new state
differs from the previous state will avoid unnecessary re-renders.
An efficient way of telling when any props or states like an array, object, etc have changed automatically is, by the immutable data structure.
The idea behind using immutable data structures is simple. For complex data types, the comparison performs over their reference. Whenever an object containing complex data changes, instead of making the changes in that object, we can create a copy of that object with the changes which will create a new reference. ES6 has object spreading operator to make this happen.
For more info on the mutable state problem, read this.