Reactivity Basics

The reactivity system is all about defining reactive values and creating reactions when these reactive values are mutated. We can create effects that will run when any referenced reactive value is changed, computed values that will be re calculated only if reactive values changed as well, we can also watch reactive values to compare with its previous value. @re-active/react package provides these functionalities which can be used inside component scope or anywhere in the application.

reactive

reactive function accepts any type of object including arrays maps and sets . Nested properties of the given object will also be reactive. It returns a reactive proxy of the object.

import { reactive, ref } from '@re-active/react';

let r1 = ref('text');
r1 = 'new text' // won't react, don't do that you will loose the reactive object
r1.value = 'new text'; // reacts

const r2 = ref(0);
r3.value = 1; // reacts

const r3 = reactive({ 
    field: 'some text',
    nested : { 
        field: 'some other text' 
    } 
});
r3.nested.field = 'new text'; // reacts
r3.field = 'new text'; // reacts

The reactive conversion is "deep": it affects all nested properties. A reactive object also deeply unwraps any properties that are refs while maintaining reactivity.

It should also be noted that there is no ref unwrapping performed when the ref is accessed as an element of a reactive array or a native collection type like Map.

To avoid the deep conversion and only retain reactivity at the root level, use shallowReactive() instead.

The returned object and its nested objects are wrapped with ES Proxy and not equal to the original objects. It is recommended to work exclusively with the reactive proxy and avoid relying on the original object.

ref

Takes an inner value and returns a reactive and mutable ref object, which has a single property .value that points to the inner value.

The ref object is mutable - i.e. you can assign new values to .value. It is also reactive - i.e. any read operations to .value are tracked, and write operations will trigger associated effects.

If an object is assigned as a ref's value, the object is made deeply reactive with reactive(). This also means if the object contains nested refs, they will be deeply unwrapped.

To avoid the deep conversion, use shallowRef() instead.

import { ref } from '@re-active/react';

const r1 = ref({ field: 'text' });
r1.value.field = 'new text'; // won't react
r1.value = { field: 'new text' }; // will react

effect

Takes a function which will only run when a referenced reactive value is changed

import { ref, effect } from '@re-active/react';

const greet = ref('Hello');

effect(() => {
    console.log(`${greet.value} World`);
})
// prints "Hello World"

greet.value = 'Hi'
// prints "Hi World"

watch

Works like effect but the callback function is called with new and old values returned by the watcher.

watch also can be given box, computed and reactive objects as the first parameter. For box and computed, value will be watched and for reactive objects the first level fields will be watched unless deep is set to true in the options. See API reference for details.

import { ref, watch } from '@re-active/react';

const spinner = ref(0);

watch(() => spinner.value, (newVal, olVal) => {
    console.log(newVal, oldVal);
})

// won't work since spinner reference is not changing, 
// value field should be accessed if it's a boxed reactive object
// otherwise it's inner fields should be accessed
watch(() => spinner, (newValue) => { console.log(newValue) })

computed

Caches and returns the value from calculation function. returned value is also reactive. Very useful for aggregating a data from various sources. Cached result is only invalidated if any of the referenced reactive values has changed.

import { reactive, computed, watch } from '@re-active/react';

const state = reactive({
    amount: 0,
    price: 10,
    productName: 'apple'
});

// computed values are lazy evaluated
const totalPrice = computed(() => {
    console.log('total price is calculated');
    return state.amount * state.price + "$";
});

state.amount = 1;
console.log(totalPrice.value);
// prints: total price is calculated;
// prints: 10$;

state.price = 20;
console.log(totalPrice.value);
// prints: total price is calculated;
// prints: 20$;

state.amount = 1; // amount is set to 1 which is the same so no invalidation for computed value
state.productName = 'banana'; // productName is not used in computed so no invalidation as well
console.log(totalPrice.value); // no re calculation, returns cached result.
// prints: 20$;

// computed values can be watched
watch(totalPrice , (newVal, oldVal) => {
    if(newVal.slice(0, -1) > 500){
        alert('Too expensive');
    }
})

more ...

In order to see complete API go to reactivity API

Last updated