Skip to content

A fine-grained reactivity (FGR) monorepo that provides primitives to transform any environment (i.e vanilla, React, etc...) into an FGR enviroment.

License

Notifications You must be signed in to change notification settings

j4ndrw/fgr-primitives

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Fine-Grained Reactivity primitives

Inspired by SolidJS and Leptos.

Important

Please, don't use this library in production. For the moment, it is purely experimental and might not scale.

Installation

NPM

npm install @j4ndrw/fgr-core

Yarn

yarn add @j4ndrw/fgr-core

PNPM

pnpm add @j4ndrw/fgr-core

ESM.SH

<html>
    ...
    <body>
        <script type="module">
            import encapsulate from "https://esm.sh/@j4ndrw/fgr-core";
        </script>
    </body>
</html>

UNPKG

<html>
    ...
    <body>
        <script type="module">
            import encapsulate from "https://unpkg.com/@j4ndrw/fgr-core/dist/index.mjs";
        </script>
    </body>
</html>

Objective

The goal of this library is to inject fine grained reactivity to any non-reactive system, as well as existing reactive systems (i.e. React, Vue, etc...). It is a bit ambitious and it's not there yet when it comes to making the primitives work in existing reactive systems, but it would be super cool to learn about what obstacles need to be overcome to achieve this goal.

After looking through Solid and Leptos, I formed an opinion that fine-grained reactivity is the most elegant and efficient paradigm / concept when it comes to building UIs, games or any other state machine!

Overview

The library provides only 1 function: encapsulate. You can think of this function as a glorified IIFE (Immediately-Invoked Function Expression).

Simple counter example

Here's an example of a counter using the encapsulate function:

import { encapsulate } from "@j4ndrw/fgr-core";

encapsulate(({ signal, effect }) => {
    const count = signal(0);

    effect(() => {
        console.log(`Count is ${count.get()}`);
    });

    for (let i = 1; i <= 5; i++) {
        count.set(i);
    }
});

This will just iterate 5 times and increment the count signal by 1. In the console, you should see a bunch of "Count is {count}", thanks to the effect.

Batch counter example

Here's the same counter example as before, but with 2 count signals, instead of one.

import { encapsulate } from "@j4ndrw/fgr-core";

encapsulate(({ signal, effect, batch }) => {
    const count1 = signal(0);
    const count2 = signal(0);

    effect(() => {
        console.log(`Count1 is ${count1.get()}`);
        console.log(`Count2 is ${count2.get()}`);
    });

    for (let i = 1; i <= 5; i++) {
        batch(() => {
            count1.set(i);
            count2.set(i * 2);
        });
    }
});

Here we're using the batch primitive to postpone running the effect until the callback function inside the batch finishes running.

As such, the effect will run 5 times, instead of 10 times without the batch.

Docs

Encapsulate

Definition

A function that provides Fine-Grained Reactivity primitives inside the callback function it takes as an argument.

Example

import { encapsulate } from "@j4ndrw/fgr-core";

encapsulate(({ signal, effect, batch, unmount }, runtime) => {
    // Your business logic goes here
});

Signal

Definition

A primitive that holds a mutable value.

Example

const count = signal(0);
count.get(); // is 0

count.set(1);
count.get(); // is 1

count.update(prev => prev + 3);
count.get(); //is 4

Effect

Definition

A primitive that runs a given callback function on mount and whenever a dependency gets updated.

Example - Effect running once

effect(() => console.log("hello"));

Example - Effect running when dependency is updated

const dependency = signal(0);

// Outputs `hello ${dependency}` 6 times:
//     - 1 time on mount
//     - 5 times since the dependency is set 5 times
//       in the for loop below
effect(() => {
    console.log(`hello ${dependency.get()}`);
});

for (let i = 1; i <= 5; i++) dependency.set(i);

Batch

Definition

A primitive that postpones running any effects for a set of dependencies until they have finished running a particular routine.

Example - Without batch

const s1 = signal(0);
const s2 = signal(0);

// Runs 11 times:
//     - once, on mount
//     - 10 times, since `s1` is set 5 times and `s2`
//       is set 5 other times
effect(() => {
    s1.get();
    s2.get();
});

for (let i = 1; i <= 5; i++) {
    s1.set(i);
    s2.set(i);
}

Example - With batch

const s1 = signal(0);
const s2 = signal(0);

// Runs 6 times:
//     - once, on mount
//     - 5 times, from the batched update
effect(() => {
    s1.get();
    s2.get();
});

for (let i = 1; i <= 5; i++) {
    batch(() => {
        s1.set(i);
        s2.set(i);
    });
}

Unmount

Definition

A primitive that runs a cleanup function and re-initializes all primitive values from the Fine-Grained Reactivity Runtime (signals, effects, etc...).

Example

unmount(() => {
    // Cleanup logic goes here
});

About

A fine-grained reactivity (FGR) monorepo that provides primitives to transform any environment (i.e vanilla, React, etc...) into an FGR enviroment.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published