SkillAgentSearch skills...

Entangle

Global state management tool for react hooks inspired by RecoilJS and Jotai using proxies.

Install / Use

/learn @bennyg123/Entangle

README

Entangle

<a href="https://app.circleci.com/pipelines/github/bennyg123/entangle"> <img src="https://badgen.net/github/status/bennyg123/entangle/main/circleci?service=github"> </a> <a href="https://www.npmjs.com/package/@bennyg_123/entangle"> <img src="https://badgen.net/npm/v/@bennyg_123/entangle?service=github " /> </a> <a href="https://bundlephobia.com/result?p=@bennyg_123/entangle@latest"> <img src="https://badgen.net/bundlephobia/minzip/@bennyg_123/entangle@latest?service=github" /> </a> <a href="https://www.npmjs.com/package/@bennyg_123/entangle"> <img src="https://badgen.net/npm/dt/@bennyg_123/entangle?service=github" /> </a>

Global state management tool for react hooks inspired by RecoilJS and Jotai using proxies.

Features

  • No need for context
  • Zero dependencies
  • Super lightweight: ~ 1kb gzipped

Table of Contents

Intro

Inspired by RecoilJS with its 3D state management where the state does not live with the virtual dom tree, I wanted to create a simpler and much more lightweight version for modern browsers (IE is dead!!). The current state management solutions in the react ecosystem work really well (mobx, redux, etc), but I think a recoil like library that allows for granular updates without context and without having to rerender the whole DOM tree is the future. Thus Entangle was born. The name Entangle comes from quantum entanglement where two Atoms are linked event across great distances and can affect each other.

This library is written in TS and has typings shipped with it.

This library should work with all browsers that support proxies (aka all modern browsers). However if you need to support other browsers there is a polyfill available, though that wont be officially supported by this library.

Please try this library out and let me know if you encounter any bugs and suggestions on improvements. Its still very much in the experimental and testing phase so try at your own risk.

<a href="https://buymeacoffee.com/bennyg123"> <img alt="Buy Me A Coffee" src="https://www.buymeacoffee.com/assets/img/custom_images/yellow_img.png" style="height: auto !important; width: auto !important;" /> </a>

Getting Started

Super simple example with makeAtom

import { makeAtom, useEntangle } from "@bennyg_123/entangle";

const atomValue = makeAtom("Hello");

const Component1 = () => {
    const [atomState, setAtomState] = useEntangle(atomValue);
    
    return (
        <div>
            <button onClick={() => setAtomState("Hello, 世界")}>Update atomState</button>
            <h1>{atomState}</h1>
        </div>
    );
}

const Component2 = () => {
    const [atomState, setAtomState] = useEntangle(atomValue);
    
    return (
        <div>
            <button onClick={() => setAtomState("Hello World")}>Update atomState</button>
            <h1>{atomState}</h1>
        </div>
    );
}

In the above example, a global atomValue is created with the initial value passed in. Then the components that need to access that value will pass in the atomValue to a useEntangle hook inside the component.

The useEntangle hook works the same way as a useState hook, the first value is the value, while the second is an updater function. If either of the buttons are clicked and they update the atomState, then both components (and only those components and their children) will rerender, staying in sync. Most importantly the parents will not rerender.

import { makeAtom, makeMolecule, useEntangle } from "@bennyg_123/entangle";

const atomValue = makeAtom("Hello");
const moleculeValue = makeMolecule((get) => get(atomValue) + " world");

const Component = () => {
    const [atomState] = useEntangle(moleculeValue);
    
    return (
        <div>
            <h1>{atomState}</h1>
        </div>
    );
}

Entangle also supports composition using atoms as well. You can pass a function to makeMolecule that takes a get method and composes the composed value using get to get the atom's current value and subscribe to those changes.

import { makeAtom, makeAsyncMolecule, useEntangle } from "@bennyg_123/entangle";

const atomValue = makeAtom("Hello");
const asyncMoleculeValue = makeAsyncMolecule(async (get) => {
    const response = await fetch(`API/${get(atomValue)}`);
    const value = await response.json(); // { value: "Hello World" }
    return value;
}, { 
    value: "Default"
}});

const Component = () => {
    const [atomState] = useEntangle(asyncMoleculeValue);
    
    return (
        <div>
            <h1>{atomState}</h1>
        </div>
    );
}

Entangle also supports async molecules as well with the makeAsyncMolecule method. You can do API calls using atom values here, and they will automatically update and subscribe to those atom changes. The value of the second parameter must match the return value of the async generator function passed in.

For example the below example wont work since you passed in a string for a default value but the async function returns an object.

import { makeAtom, makeAsyncMolecule, useEntangle } from "@bennyg_123/entangle";

const atomValue = makeAtom("Hello");
const asyncMoleculeValue = makeAsyncMolecule(async (get) => {
    const response = await fetch(`API/${get(atomValue)}`);
    const {value} = await response.json(); // { value: "Hello World" }
    return { response: value };
}, "HELLO WORLD");

const Component = () => {
    const [atomState] = useEntangle(asyncMoleculeValue);
    
    return (
        <div>
            <h1>{atomState}</h1>
        </div>
    );
}

For this reason it is better to add explicit types (if you are using TS) to the make methods:

import { makeAtom, makeMolecule, makeAsyncMolecule, useEntangle } from "@bennyg_123/entangle";

const atomValue = makeAtom<string>("1");
const moleculeValue = makeMolecule<number>((get) => parseInt(get(atomValue)));
const atomValue = makeAsyncMolecule<{value: string}>(async (get) => ({value: get(atomValue)}));

<hr /> ## API <h3 id="make-atom"><code>makeAtom</code></h3>

makeAtom creates an atom value to be used inside the useEntangle hook. All components using this value will be synced and updated when any of the components update the atom. It does not matter how deeply nested.

import { makeAtom, useEntangle } from "@bennyg_123/entangle";

const atomValue = makeAtom("Hello");

const Component = () => {
    const [atomState, setAtomState] = useEntangle(atomValue);
    
    return (
        <div>
            <button onClick={() => setAtomState("Hello, 世界")}>Update atomState</button>
            <h1>{atomState}</h1>
        </div>
    );
}
<h3 id="make-molecule"><code>makeMolecule</code></h3>

makeMolecule allows for subscriptions to an atoms changes for composing values based off other atoms.

import { makeAtom, makeMolecule, useEntangle } from "@bennyg_123/entangle";

const atomValue = makeAtom("Hello");
const atomValue2 = makeAtom("world");

// In the below example, you can pass in am optional boolean as a second argument to the getter, this will un subscribe the molecule from that atoms changes
const moleculeValue = makeMolecule((get) => get(atomValue) + get(atomValue2, false));

const Component = () => {
    const [atomState, setAtomState] = useEntangle(atomValue);
    const [moleculeState] = useEntangle(moleculeValue);
    
    return (
        <div>
            <button onClick={() => setAtomState("Hello, 世界")}>Update atomState</button>
            <h1>{atomState}</h1>
            <h1>{moleculeState}</h1>
        </div>
    );
}

It is important to note that since molecules are dependent on atoms. They are read only, thus while they can be used with useEntangle, calling the set function will throw an error. As a result they should be used with useReadEntangle

import { makeAtom, makeMolecule, useEntangle } from "@bennyg_123/entangle";

const atomValue = makeAtom("Hello");
const moleculeValue = makeMolecule((get) => get(atomValue) + " world");

const Component = () => {
    const [atomState, setAtomState] = useEntangle(atomValue);
    const [moleculeState, setMoleculeState] = useEntangle(moleculeValue); // not recommended
    const readOnlyMoleculeState = useReadEntangle(moleculeValue);
    
    return (
        <div>
            <button onClick={() => setAtomState("Hello, 世界")}>Update atomState</button>
            <button onClick={() => setMoleculeState("Hello, 世界")}>Throws an error</button>
            <h1>{atomState}</h1>
            <h1>{moleculeState}</h1>
            <h1>{readOnlyMoleculeState}</h1>
        </div>
    );
}
<h3 id="make-async-molecule"><code>makeAsyncMolecule</code></h3>

Same usage as makeMolecule except you pass in an async function and a default value as the second argument.

import { makeAtom, 
View on GitHub
GitHub Stars28
CategoryDevelopment
Updated1y ago
Forks1

Languages

TypeScript

Security Score

80/100

Audited on Oct 30, 2024

No findings