Sponsor
Star

Created With

linkChange

States in RxDeep keep their values updated using Change objects. A Change object has the following properties:

1linkinterface Change<T> {

2link value: T | undefined; // --> the value of the change

3link trace?: ChangeTrace<T> | undefined // --> the trace of the change

4link}

The .value property denotes the value that a receiving state should update to, and the .trace property denotes the trace of the change, relative to current state's position in the state-tree.


linkChange Trace

The trace has a tree structure, with leaf nodes denoting changes directly to a state, and non-leaf nodes denoting changes to some of the properties / indexes of the value of a state. Each of these nodes also is represented by a trace object.

When representing a leaf node, we say the change trace is leaf, which means is of the following type:

1linktype ChangeTraceLeaf<T> = {

2link from: T | undefined; // --> change from this value

3link to: T | undefined; // --> change to this value

4link}

Changing the value of a state, is equivalent of creating a change object with a leaf trace and sending it up the state's upstream. So this:

1links.value = x;

is equivalent of this:

1links.upstream.next({

2link value: x,

3link trace: { from: s.value, to: x }

4link});


When not representing a leaf node, the change trace is of the following format:

1link// if T is an array:

2linktype ChangeTraceNode<T> = {

3link subs: {

4link [index: number]: ChangeTrace<T[number]>;

5link }

6link}

7link

8link// if T is an object:

9linktype ChangeTraceNode<T> = {

10link subs: Partial<{

11link [K in keyof T]: ChangeTrace<T[K]>;

12link }>

13link}

Which means if T is an array, then the change trace's property .subs is a partial mapping of its indexes to change traces of its elements, and when its an object, then the change trace's property .subs is a partial mapping of its properties to change trace of those properties:

1linkimport { state } from 'rxdeep';

2link

3linkconst s = state({

4link x: { y: false },

5link z: 3

6link});

7link

8links.sub('x').sub('y').subscribe(console.log);

9link

10links.upstream.next({

11link value: { x: { y: true }, z: 3 },

12link trace: {

13link subs: {

14link x: {

15link subs: {

16link y: { from: false, to: true }

17link }

18link }

19link }

20link }

21link});

22link

23link// Logs:

24link// > false

25link// > true


linkCreating Change Objects

You can utilize the change() function to create a Change object from given value to given value:

1linkimport { change } from 'rxdeep';

2link

3linkconst c = change(

4link {

5link x: { y: false },

6link z: 3

7link },

8link {

9link x: { y: true },

10link z: 3

11link }

12link);

13link

14linkconsole.log(c);

15link

16link// Logs:

17link// > {

18link// > value: { x: { y: true }, z: 3 },

19link// > trace: {

20link// > subs: {

21link// > x: {

22link// > subs: {

23link// > y: { from: false, to: true }

24link// > }

25link// > }

26link// > }

27link// > }

28link// > }


This allows updating multiple parts of the state-tree at once in an efficient manner, by creating a change object and sending up a state's upstream instead of manually changing sub-state values:

1links.upstream.next(change(s.value, _NewValue));


You can also create only a change trace (instead of a full change object) using the trace() function:

1linkimport { trace } from 'rxdeep';

2link

3linktrace(X, Y);


linkReverting Changes

You can use the reverse() function to create a change that would reverse the given change:

1linkimport { reverse, change } from 'rxdeep';

2link

3linkconst c = change(X, Y);

4linkconst r = reverse(c);


The reverse() function is actually used by VerifiedState to revert rejected changes on the sub-tree.


linkRecording Changes

As discussed here, each state is basically a downstream of changes (coming from parent state) and an upstream of changes (the state announcing changes to parent state). These literally are:

1linkclass State<T> {

2link ...

3link readonly downstream: Observable<Change<T>>;

4link readonly upstream: Observer<Change<T>>;

5link}

With Observer and Observable being the primary RxJS concepts. This means you can easily tap into the downstream of any state and record its changes, or push recorded or manufactured changes to a state's upstream.

However, specifically for recording changes, you should note that RxDeep typically avoids creating new objects and makes a lot of changes in-place. This means that if you are only holding a reference to a particular change object, its actual properties might get altered. To avoid that, it is highly recommended to copy the change objects you capture, either via JSON serialization / deserliaziation or using deep copy functions such as lodash.cloneDeep().

ChangeChange TraceCreating Change ObjectsReverting ChangesRecording Changes

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