So I read this article about Angular 2 change detection, but after reading it I have got more confused, so I started reading some of the comments which led to more confusion.
I don't get it how Observables are quite different from Immutables and in this specific case of the Todo app, which approach is better?
Imagine that you have a tree of nodes, and each node has a bit that says "I may have changed". With Observable
, we are talking about events getting emitted; the event is dispatched up the tree. In other words:
Todo_ChangeDetector
would be flagged,Todos_ChangeDetector
would be flagged,App_ChangeDetector
would be flagged,Imagine you have 2 todo lists:
Which grocery list is active you are currently showing would be the Observable
; e.g. a dropdown, nav pills, etc. All the todo items under those 2 lists could be Immutable
; e.g. you are not the meeting organizer and cannot change the meetings.
So, like we are 5 year olds from now on.
This is JohnCmp
. John is a component that accepts two inputs, a name: {first: 'John', last: 'Smith'}
and an age: 20
.
What happens if name
is mutable? Well, someone could pass it to John
and hold a reference to it. So that John, and ANY number of other objects or services can hold a reference to that name
object. This means they can change it, say doing name.last = 'foo'
. And John's name changes now, but he didn't receive a new name. He still has a reference to that name
object, but it mutated.
If we want to detect this, we have to aggressively check name, name.first, name.last, and so on with every property of every object we pass around. How much easier would it be to do name === newName
and just compare references, hey? If name is immutable, we don't need to go crazy and check every property, we can check references and know if an object is changed quickly.
Ok, now, imagine nobody holds a reference to John
's name object. So if they want to give him a new name, they must pass in a NEW name object. Then John
changes only when his input changes. This is what is meant here:
If a component depends only on its input properties, and they are immutable, then this component can change if and only if one of its input properties changes. Therefore, we can skip the component’s subtree in the change detection tree until such an event occurs.
Alright, so that's the basic case right? Now we don't have to worry about checking every property, we just check that references haven't changed. Much improved! BUT objects can be large. So a people
array is an immutable Array
of John
s, which are immutable, of name
s that are also immutable. So in order to change John's name. you need to produce a new name, and a new John, and a new people array.
So they all change, the whole tree needs to be traversed. So that's O(n)
. Hence this little comment:
If you have a tree of components with immutable bindings, a change has to go through all the components starting from the root.
BUT
This is not the case when dealing with observables.
Why tho?
Observables emit events. They don't need to change everything like immutables, they just need to emit
a change event. So in his example, you don't have an immutable "array" that you need to recreate and therefore re-evaluate all the changes up the tree. Now you have a people
observable, that triggers events when it changes. And John
receives an observable as well.
So you can just trigger an event in John
telling him that his name changed, WITHOUT triggering an event in people
or things of the sort. Avoiding you to re scan everything for changes, thus reducing complexity to O(log n)
, as per this quote:
As you can see, here the Todos component has only a reference to an observable of an array of todos. So it cannot see the changes in individual todos.
(emphasis mine).
Hope this helps.