🚀 Getting Started

Installation

If you don't have RxJS already, you'll need to install it first npm install rxjs, and then

npm i @activejs/core

If you don't have access to a module bundler, here's the UMD bundle link. (not needed for frameworks like Angular, React, Vue etc.)

Prerequisite

ActiveJS assumes a prior understanding of Observables. Some understanding of RxJS can also be helpful, but the basic knowledge of Observables should be enough. This official guide or this primer might help you get up to speed.

Prelude

Imagine a variable that can only be assigned a value of a particular data type, let's say only number, and when we change the value of this variable, it gets broadcasted to observers, while we also keep past values in the cache to be able to undo/redo. That's basically what an ActiveJS Unit allows us to do, and the one that only allows numbers is called NumUnit.

At the heart of ActiveJS are these reactive data structures called Units, that emulate JavaScript's native data structures. Units behave almost exactly like a native data structure would, while the actual value is kept encapsulated inside the Unit. Units are observable and every operation and mutation is broadcasted.

Quick Example:

Let's take a simple and comparable example of implementing a counter. We'd use a NumUnit for the counter since we expect the value to always be a number.

// initialize a NumUnit.
const counterUnit = new NumUnit({initialValue: 6});
// NumUnit has default initial value 0,
// you can also provide an initial value.
// observe the Unit for current and future values
counterUnit.subscribe(value => console.log(value))
// logs 6 immediately and will log future values
// or, directly access the current value
console.log(counterUnit.value()); // logs 6
// define two pure functions that produce a new value
const increment = value => value + 1;
const decrement = value => value - 1;
// now we'll use the above pure functions as value-producers,
// the dispatch method expects a value or a value-producer-function
counterUnit.dispatch(increment); // makes the value 7
counterUnit.dispatch(decrement); // makes the value 6 again
// or just directly pass the value
counterUnit.dispatch(7); // makes the value 7
// try an invalid value
counterUnit.dispatch('20'); // NumUnit will ignore this invalid string value
// so the value is still 7

Try it on StackBlitz. Or, let's also try some built-in methods.

ActiveJS Units are cache-enabled, and by default, every Unit caches two values. When you navigate through the cache, the cache stays intact, while the value changes. This makes it very easy to travel back in time and then go back to the future.

// to recap, the list of cached values is [6, 7] at the moment
// and the current value is 7
// go back to the previous value
counterUnit.goBack(); // now value is 6 (cache isn't affected)
// go forward to the next value
counterUnit.goForward(); // now value is 7 (cache isn't affected)
// clear the value
counterUnit.clearValue(); // now value is 0 (the default value for NumUnit)
// reset the value
counterUnit.resetValue(); // now value is 6 again (the initial-value)

There are even more built-in things that a Unit can do, but this is the crux of it.

Fundamentals

Other constructs within ActiveJS build on the core concept of independent storage Units and work together to create a complete workflow for advanced use cases.

ActiveJS is made up of four fundamental constructs, visualized with their core components in the diagram below. Each one of them plays a different yet crucial role in the complete state-management lifecycle.

Bird's eye view of ActiveJS constructs.
  • Units are independent, reactive data structures. Units are responsible for holding the state of your App.

    • Units are cache-enabled, and based on JavaScript's native data structures like boolean, string or array.

    • For example, ListUnit is an elaborate array like construct which is also an Observable and implements all the Array.prototype methods in a way that, any mutation caused by these methods, emits a new array with the mutations applied to it.

    • All available Units are BoolUnit, NumUnit, StringUnit, ListUnit, DictUnit, and GenericUnit.

  • Systems encompass a certain number of Units in a specialized way and implement inter-relationships among these Units to pertain to a specific task. For now, there's only one of its kind.

    • AsyncSystem helps with async APIs like XHR or fetch, it makes sharing their state very easy.

  • Actions are meant to represent unique events as Observables. Action is an RxJS Subject like construct, with extra features like replay on demand. Actions are useful, but not essential to start using ActiveJS.

  • Cluster is literally a cluster, a wrapper, that can be used to create a group of Units, Systems, Actions as well as Clusters. It creates a master Observable of the combined value of its members, and also provides direct static access to the combined value.

To jump right into the action without writing any code, you can try out this visual playground, it might give you some more perspective, and help understand the most important ActiveJS constructs Units, and Systems.

Do I need ActiveJS?

As much as we'd like to say YES, we don't want you to get into something that you later realize is not for you.

That being said, If you're coming from a Flux background you'll see that it's different yet somehow familiar and easier. If you feel like Flux based state management solutions are overkill, you'll love it. If you're happy with RxJS, you'll be able to do more by doing less. If your single-page app is not using Observables, it can give you wings (maybe).

So, do you need it? The answer is we are not sure, it can definitely help you in one way or another, you can start small without much commitment, give it a go and see how it pans out.