Created With


RxDeep is designed to be extremely precise, meaning states should emit only when they have a good reason to. To be more precise (pun unintended), absolute precision means a state emits values when and only when one of the following holds:

  1. Its value has changed.

  2. Its value is directly updated (possibly to the same value).

  3. It points to the same address in state-tree as another state whose value is directly updated (possibly to the same value).

  4. One of its descendent sub-states (a sub-state in its sub-tree) satisfies (2) or (3).

1linkconst root = state([

2link { name: 'Jack', address: { city: 'Java', country: 'Indonesia' } },

3link { name: 'Jafar', address: { city: 'Jiroft', country: 'Iran' } },



6linkconst mid1 = root.sub(0);

7linkconst mid2A = mid1.sub('address'); // --> shares tree address with mid2B

8linkconst mid2B = root.sub(0).sub('address'); // --> shares tree address with mid2A

9linkconst leaf1 = root.sub(0).sub('address').sub('city'); // --> shares tree address with leaf2

10linkconst leaf2 = mid2A.sub('city'); // --> shares tree address with leaf1


12linkleaf1.value = leaf1.value; // --> issue update without value change


14link// As a result:

15link// > leaf1 will emit (due to (2))

16link// > leaf2 will emit (due to (3))

17link// > mid2A, mid2B, mid1, and root will emit (due to (4))

By this definition RxDeep is absolutely precise if object immutabilty is respected and objects passed to it are of plain (non-circular) JavaScript objects.


A Loss is when a State should emit (according to the outlined criteria) but it doesn't. RxDeep is not lossy by default, only IF object immutability is respected. Simply put, you must not change the value of a State without changing its reference, as otherwise there is no mechanism for RxDeep to pickup those changes and distinguish what has changed.

Learn More


Redundancy refers to situations when a state emits without a good reason for doing so (i.e. none of the outline criteria hold up). RxDeep has no redundancy, meaning a state does not emit values without a good reason (without one of the aforementioned criteria being true).

This is due to the fact that leaf-changes are fully traced and delivered only to affected sub-states, and arbitrary changes are traced until their issuing depth and then post-traced so that the change trace is complete until leafs of the state-tree, allowing for precise propagation of the change.


As discussed here, a leaf-change has O(log(n))\Omicron(\log(n)) complexity and an arbitrary change at depth δ\delta and above a sub-tree of nδn_{\delta} nodes has O(log(n)+nδlog(nδ))\Omicron(\log(n) + n_{\delta}\log(n_{\delta})) time complexity. For most day to day use cases both of these are more than fast enough.

However, in special cases you might need that additional performance. As (also) discussed here, if the whole sub-tree needs to change, you need minimum of O(log(n)+nδlog(nδ))\Omicron(\log(n) + n_{\delta}\log(n_{\delta})) operations, as you need that many emissions to not be lossy. However, if your change affects a bounded number of leaf states, for example kk leaf states, then the minimum number of operations is given by:

O(log(n)+(k1)log(nδ))=O(log(n))\Omicron(\log(n) + (k - 1)\log(n_{\delta})) = \Omicron(\log(n))1

A simple method of achieving that performance is identifying the kk leaf nodes and applying change to them. Time-complexity of this solution is given by:

O(klog(n))=O(log(n))\Omicron(k\log(n)) = \Omicron(\log(n))1

However, this also results in kk emissions by all affected states (e.g. the root state will also emit kk times).

1linkconst company = state({

2link teams: [{

3link people: [{name: 'Jack', age: 42}, {name: 'Jill', age: 31}],

4link name: 'Awesome Team',

5link }, ...]




9link// Find all affected leaf changes and apply changes directly to them.


11linkcompany.sub('teams').sub(0).sub('people').sub(0).sub('name').value = 'Jafar';

12linkcompany.sub('teams').sub(0).sub('name').value = 'Pro Team';

A more efficient solution would be to:

  1. Identify the top-most common ancestor of all affected leaf nodes,

  2. Apply changes respecting maximal object immutability.

1linkconst company = state({

2link teams: [{

3link people: [{name: 'Jack', age: 42}, {name: 'Jill', age: 31}],

4link name: 'Awesome Team',

5link }, ...]




9link// This is the top-most common ancestor:


11linkconst target = company.sub('teams').sub(0);



14link// Now lets apply changes with maximal object immutability:


16linktarget.value = {

17link ...target.value,

18link name: 'Pro Team',

19link people: [

20link {

21link ...target.value.people[0],

22link name: 'Jafar'

23link }

24link ...target.value.people.slice(1),

25link ]


Object immutability means that the reference of an object is changed IF its value has changed.
Maximal object immutability means the reference of an object is changed IF AND ONLY IF its value has changed.

The post-tracing algorithm of RxDeep makes quick reference checks to rule out identical objects, so when making a change respecting maximal object immutability, and with the aforementioned criteria holding (constant kk leafs are actual subjects of the change), post-tracing consumes O(klog(nδ))\Omicron(k\log(n_{\delta})) operations, so the complexity of overall change propagation is given by:

O(log(n)+(k1)log(nδ))=O(log(n))\Omicron(log(n) + (k - 1)log(n_{\delta})) = \Omicron(log(n))1

Additionally, with this solution affected states emit exactly once.


Home How to Install State Key Tracking Change Verification Change Performance Precision