Skip to content

Store

The store API provides a way to create deep reactive wrappers for arbitrary objects.

The wrap function creates a deep reactive wrapper:

import { wrap } from "rvx/store";

const state = wrap({
    message: "Hello World!",
});

<h1>{() => state.message}</h1>
import { wrap, e } from "./rvx.js";

const state = wrap({
    message: "Hello World!",
});

e("h1").append(() => state.message)

By default, Arrays, Maps, Sets and Objects without any or with the Object constructor are reactive. Anything else is returned as is.

Updates

To update a reactive object, you can directly modify the wrapper.

import { wrap } from "rvx/store";

const todos = wrap([
    { name: "Foo", done: false },
    { name: "Bar", done: false },
]);

todos[1].done = true;
todos.push({ name: "Baz", done: true });
import { wrap } from "./rvx.js";

const todos = wrap([
    { name: "Foo", done: false },
    { name: "Bar", done: false },
]);

todos[1].done = true;
todos.push({ name: "Baz", done: true });

Note, that every individual update is processed immediately. To prevent this, you can use batches:

import { batch } from "rvx";

batch(() => {
    todos[1].done = true;
    todos.push({ name: "Baz", done: true });
});
import { batch } from "./rvx.js";

batch(() => {
    todos[1].done = true;
    todos.push({ name: "Baz", done: true });
});

Signal Reflection

The reflect utility can be used to create a signal that reflects a reactive property of an arbitrary object.

import { reflect, wrap } from "rvx/store";

const item = wrap({ name: "Foo", done: false });

const done = reflect(item, "done");
import { reflect, wrap } from "./rvx.js";

const item = wrap({ name: "Foo", done: false });

const done = reflect(item, "done");

The target object doens't need to be a reactive wrapper. Any arbitrary object with reactive properties works.

Classes

By default, arbitrary class instances are not reactive unless you specify, how to wrap them:

import { wrapInstancesOf } from "rvx/store";

class Example {
    static {
        // Wrap instances of "Example" in the same way, objects are wrapped:
        wrapInstancesOf(this);

        // Or implement custom behavior:
        wrapInstancesOf(this, target => {
            return new Proxy(target, ...);
        });
    }
}
import { wrapInstancesOf } from "./rvx.js";

class Example {
    static {
        // Wrap instances of "Example" in the same way, objects are wrapped:
        wrapInstancesOf(this);

        // Or implement custom behavior:
        wrapInstancesOf(this, target => {
            return new Proxy(target, ...);
        });
    }
}

Private Fields

Private fields are not reactive. Also, you need to ensure they are accessed through the original object instead of reactive wrappers by using unwrap.

import { wrapInstancesOf, wrap, unwrap } from "rvx/store";

class Example {
    static {
        wrapInstancesOf(this);
    }

    #count = 0;

    thisWorks() {
        // "unwrap" always returns the original object
        // or the value itself if it isn't a wrapper:
        unwrap(this).#count++;
    }

    thisFails() {
        // This will fail, since "this" refers to the
        // reactive wrapper instead of the original object:
        this.#count++;
    }
}

const example = wrap(new Example());
example.thisWorks();
example.thisFails();
import { wrapInstancesOf, wrap, unwrap } from "./rvx.js";

class Example {
    static {
        wrapInstancesOf(this);
    }

    #count = 0;

    thisWorks() {
        // "unwrap" always returns the original object
        // or the value itself if it isn't a wrapper:
        unwrap(this).#count++;
    }

    thisFails() {
        // This will fail, since "this" refers to the
        // reactive wrapper instead of the original object:
        this.#count++;
    }
}

const example = wrap(new Example());
example.thisWorks();
example.thisFails();