Immutability

ActiveJS Units that store primitive values i.e. BoolUnit, NumUnit, and StringUnit are automatically immutable because these Units deal with primitive values, and primitive values in JavaScript are immutable.

The Units that store non-primitive values i.e. DictUnit, ListUnit, and GenericUnit can be made immutable by just setting a configuration flag to true.

Creating an immutable Unit

const immutableDict = new DictUnit({immutable: true});
// we're done, it's immutable now

Making all Units immutable

All the Units can be made immutable by default, by setting a flag in the global configuration.

Configuration.set({
    UNITS: {immutable: true}
});
// all the Units initialized after this will be immutable,
// if not configured otherwise

const immutableList = new ListUnit(); // immutable

// override global config
const mutableList = new ListUnit({immutable: false}); // mutable

Efficiency and precautions

Immutability brings a lot of benefits, such as predictability, and improving JS frameworks' rendering performance by avoiding the need for dirty checking, etc.

However, if not used correctly, there is a chance that you might run into performance bottlenecks.

Although ActiveJS takes most of these precautions on your behalf and tries to give the best experience and performance without any significant change in development practices and flow. There are still a few things that you might want to be aware of.

When a Unit is immutable:

  1. Avoid using the static value access as much as you can, since the value() method creates a new copy every time you call it. Instead use Observable value, which is shared with all the observers.

  2. Don't use the value() method directly in an Angular template, otherwise on every change detection there will be a new copy. Instead use the Units with async pipe.

  3. Prefer using ListUnit and DictUnit over GenericUnit, and use methods like set, get, findByProp, etc. that do not create a copy on their invocation.

  4. When dispatching a value using the dispatch method, you can mutate the value provided to the value-producer function, because it's a copy. And if you don't intend to use the current value to create a new value, just pass the new value directly so that the Unit doesn't waste a copy.

  5. When you need to invoke a method like find which uses a predicate method to loop over stored items, if you can ensure that you won't mutate the value, you can invoke these methods on raw value instead, without creating a copy by using Unit.rawValue() e.g:ListUnit.rawValue().find(item => item.name.startsWith('A'))

In general, just try to avoid methods that create a new copy, and use alternative methods that do not. You can check this information in the documentation available within the library, or you can refer to API reference.

Supported data types

It's advised to put only serializable data types in an immutable Unit, non-serializable data types like Map, Set, etc. are not cloned by ActiveJS. They will be left as is and will be shared by reference (i.e. will be mutable).

All the primitive data types like boolean, number, etc. are supported by default, along with array and simple object that can be cloned by ActiveJS, and hence can be guaranteed to be immutable.

Last updated