A blend of @preact/signals-core and solid-js basic reactivity API

Overview

µsignal

Downloads Coverage Status build status

Social Media Photo by Carlos Alberto Gómez Iñiguez on Unsplash

A blend of @preact/signals-core and solid-js basic reactivity API, with API and DX mostly identical to @preact/signals-core but extra goodness inspired by solid-js, 726 bytes minified with brotli.

import {signal, computed, effect, batch, Signal} from 'usignal';
// const {signal, computed, effect, batch, Signal} = require('usignal');

signal(0) instanceof Signal;          // true
computed(() => {}) instanceof Signal; // true

effect(
  () => { console.log('fx') },
  void 0,       // optional value to pass along the callback as initial/prev value
  {async: true} // optionally make the effect async: false by default
);

// try every example shown in
// https://github.com/preactjs/signals
// or see test/index.js file to see it in action

Exports

This is a dual module so it works in either CommonJS or ECMAScript module systems.

  • usignal/sync exports with an enforced sync effect
  • usignal/async exports with an enforced async effect
  • usignal in browsers exports usignal/async and usignal/sync in servers or by default
  • usignal/core just exports the effect as callback that accepts an effect and an optionally asynchronous true flag, used by all other exports by default, but you decide if a specific effect should sync or async.
  • the unpkg/usignal default export points at the pre minified es.js file without any enforcement around effect, like usignal/core, so that all effects are sync by default but can be async passing true as second parameter

Current exports are exactly these:

import {
  signal,
  computed,
  effect,
  batch,
  Signal
} from 'usignal';

The Signal export is useful only as brand check for either computed or signal references, but it cannot be used as constructor right away.

Exports - Extra

To allow developers to tray and use different patterns there are a few variants of this module, still based on the very same core primitives:

  • usignal/fn, with its */sync and */async variants, where signals are callbacks so that signal() returns a its value, and signal(value) updates its value and return the new one, inspired by S. Comouteds do not update anything so computed() returns values. This is a variant around the .value accessor pattern I don't necessarily disike, specially when we'd like to signal that a signal is being observed: effect(() => { mySignal(); })
  • usignal/solid, with its */sync and */async variants, where the module exports createEffect, createMemo, and createSignal, mimicking the behavior (and returned values) as solid-js basic reactivity API. This is handy to compare the two or drop-in usignal in solid-js already based code.

Differently thought ...

  • the default comparison for equality is not based on === but on Object.is. This might be a tiny, hopefully irrelevant, performance penalty, but I feel like guarding NaN cases in reactivity is a step forward to avoid infinite loops out of NaN poisoning some computation. +0 and -0 are less interesting cases to tackle, still these might be fundamental in some case, hence preserved in this moudle.

  • this library has lazy, non side-effecting, computed values, something @preact/signals-core recently introduced and Solid 2.0 is planning to improve.

  • computed accepts an initial value otherwise passed as previous one by default, mimicking solid-js useMemo(fn[, value[, options]]) signature.

  • both signal(value[, options]) and computed(fn[, value[, options]]) accept an optionally options argument, currently implementing equals as explained in silid-js documentation.

  • both signal and computed also return a thenable instance that can be used to await signal or await computed without needing to use await signal.value or await computed.value out of this poll.

  • both signal and computed also have a toJSON helper abd a valueOf() able to implicitly use their values, e.g.

const one = signal(1);
const two = signal(2);
const three = computed(() => one + two);

three.value;  // 3 indeed!

Benchmark

The benchmark currently compares S, solid, preact/signals, and cellx against usignal.

Please note preact is currently not able to solve nested effects so its logic might be simpler than other libraries.

npm run benchmark

current status

Tests

This module is 100% code covered, including the WeakRef possible leaks which is tested through the test/leak.js file, which is part of the build script process.

To use other libraries as reference, I have also added preact/signals-core and solid-js dev-dependencies within the test folder.

Please note preact is currently not able to solve nested effects so its logic might be simpler than other libraries.

The following instructions are needed to test other libraries too:

cd usignal
npm i
cd test
npm i
cd ..

# normal tests
npm test usignal      # shows also code-coverage
npm test solid
npm test preact

# leak test
npm run leak usignal  # calculate leaks via internals
npm run leak solid
npm run leak preact

About the leak test

This file is not meant at all as meaningful benchmark against other libraries, it's simply there to allow me to spot regressions on future updates of the library:

  • there should be zero leaks on signals when a computed reference is garbage collected v0.5.0 removed the WeakRef, computeds go when signals go ... but why?!
  • the amount of used memory should always be lower than the initial one
  • the performance should be competitive compared to others
Comments
  • Reactive benchmark

    Reactive benchmark

    https://codesandbox.io/s/oby-bench-cellx-forked-mqlv4x?from-embed

    If I select 5000 for usignal it says Maximum call stack size exceeded. Why is that? If i select sinuous and put 50000 also doesn't break anything.

    wontfix 
    opened by kethan 14
  • Update benchmark numbers for @preact/signals-core

    Update benchmark numbers for @preact/signals-core

    We've just released a new version of @preact/signals-core where we reworked the internals. With the new version the numbers don't reflect the measurements in the readme anymore. These are the results I get on my machine (M1 Pro) with a fresh clone from usignal.

    Screenshot 2022-09-21 at 18 15 04
    opened by marvinhagemeister 11
  • Nested effects.

    Nested effects.

    Wow! Awesome I was expecting a micro signal from you there it is. I have an issue if I nest effects like this:

    const counter = signal(1);
    const double = computed(() => counter.value * 2);
    const tripple = computed(() => counter.value * 3);
    
    effect(() => {
      console.log(double.value);
      effect(() => {
        console.log(tripple.value);
      });
    });
    counter.value = 20;
    

    The triple value is consoled 3 times.

    opened by kethan 11
  • Fixed types

    Fixed types

    This added @protected to Signal._, since Signal._ is used within subclass. https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#property-modifiers

    opened by 38elements 5
  • Conditional Nested Effects

    Conditional Nested Effects

    Not sure if this is relevant for µsignal, please close if not.

    @preact/signals output:

    Activating: red
    Red is off
    Red is on
    Activating: green
    Green is on
    Green is off
    Activating: blue
    Blue is off
    Blue is on
    Activating: red
    Red is on
    

    µsignal

    Activating: red
    Red is off
    Red is on
    Activating: green
    Activating: blue
    Activating: red
    

    For details see: https://gist.github.com/peerreynders/a942a2b4dac89f2e5eda456693a720ea

    opened by peerreynders 5
  • [Question]: Plan to support nested object as initialState

    [Question]: Plan to support nested object as initialState

    thanks for this lightweight signal base library, Is there any plan to support nested object as initialState? if this is out of scope its fine :) you can close this github issue

    opened by aelbore 2
  • usignal v0.7.0

    usignal v0.7.0

    Breaking Changes

    • effect signature has changed into effect(fn[, value[, options]]) and unless you were using sync or async export this will break previous code/examples. The signature is now aligned with solid-js createEffect one, plus an optional options argument to define the async behavior, still false by default.

    New Features / Signatures

    • signal signature now accepts an optional options argument like it is for solid-js createSignal utility. It is now possible to define the equals field as true (default), false (always signal updates), or (prev, next) => boolean callback to allow edge cases or deeper comparisons among "states".
    • computed signature now accepts also an optional initial value to pass along during the first time the computed gets executed, updated automatically as the previous value, whenever accessed later on while computing. It also accepts an optional options argument to replicate solid-js createMemo utility.
    • effect signature now accept an optional initial value like computed, plus an optional options object which, currently, only supports the {async: ...} field.
    opened by WebReflection 1
  • v0.6.0 - Runtime computeds

    v0.6.0 - Runtime computeds

    • signals dependencies graph changed from first-call static to dynamic
    • removed unnecessary loop for related signals (perf++)
    • added test conformed to developers expectations around dynanimc signals
    opened by WebReflection 1
  • Return type of effect

    Return type of effect

    The return type of effect is marked as void but the callback is returned, hence it probably should be typed as () => void

    https://github.com/WebReflection/usignal/blob/a7d634a6bee97a9ffc334aa33c9f74c317df1c7b/esm/index.js#L141

    opened by benmerckx 0
  • Expose .subsribe method?

    Expose .subsribe method?

    @preact/signals expose .subscribe method as part of external interface (although undocumented), which makes it effectively a Subscribable - pattern shared by Rxjs, Observable and many variations.

    Would that be within usignal's intention to get this uniformity feature by exposing that method?

    opened by dy 9
Owner
Andrea Giammarchi
Web, Mobile, IoT and all Web & JS things since 00's
Andrea Giammarchi
A mini version of Vue 3 with reactivity, runtime, and compiler modules

A mini version of Vue 3 with reactivity, runtime, and compiler modules

Boquan (Brian) Yin 29 Jun 16, 2022
genji is A small vue state management framewok by vue3 reactivity.

genji is A small vue state management framewok by vue3 reactivity. Why calls genji ? It's inspired by Overwatch. Genji flings prec

xieyezi 13 Sep 5, 2022
Write components once, run everywhere. Compiles to Vue, React, Solid, Angular, Svelte, and more.

Write components once, run everywhere. Compiles to: At a glance Mitosis is inspired by many modern frameworks. You'll see components look like React c

Builder.io 7.4k Dec 7, 2022
Solid hooks for Firebase v9.

solid-firebase Solid hooks for Firebase. Quick start Install it: yarn add firebase solid-firebase Configure firebase app: import { render } from 'soli

Robert Soriano 38 Nov 25, 2022
hCaptcha Component Library for Solid

hCaptcha is a drop-replacement for reCAPTCHA that protects user privacy, rewards websites, and helps companies get their data labeled.

Mikkel RINGAUD 5 Nov 7, 2022
🚧 Constructive solid geometry for React

?? Constructive solid geometry for React

Poimandres 83 Nov 30, 2022
A description of React's new core algorithm, React Fiber

React Fiber Architecture Introduction React Fiber is an ongoing reimplementation of React's core algorithm. It is the culmination of over two years of

Andrew Clark 9.8k Dec 4, 2022
Simple and powerful state management in React and Preact

Remini Simple and powerful state management in React and Preact Easy to learn Small and quick From tiny to complex apps Get started At first you have

Slava Bereza 13 Dec 2, 2022
Revolt client built with Preact

revolt This is the web client for Revolt, which is also available live at app.revolt.chat. Official screenshots of the client are available in this Go

REVOLT 8 Sep 26, 2021
Revolt client built with Preact

Revite This is the web client for Revolt, which is also available live at app.revolt.chat. You can track progress on the client on our Wekan board. Of

REVOLT 614 Dec 2, 2022
An all in one preset for writing Preact apps with the vite bundler.

@preact/preset-vite An all in one preset for writing Preact apps with the vite bundler. Features: Sets up Hot Module Replacement via prefresh Enables

Preact 174 Dec 3, 2022
In this assignment you will start with a basic calculator and style it using CSS, inline styles, and styled-components.

In this assignment you will start with a basic calculator and style it using CSS, inline styles, and styled-components.

Brandon Cloutier 0 Oct 12, 2022
This project features basic simulations that show the effects of social distancing

This project features basic simulations that show the effects of social distancing

Veselin Stoyanov 2 Feb 1, 2022
Basic React wrapper for the Leaflet Geoman Plugin

Basic React wrapper for the Leaflet Geoman Plugin

Derick M. 7 Oct 30, 2022
React Query Typed Api - An opinioneted wrapper around react-query to implement fully typed api requests

React Query Typed Api - An opinioneted wrapper around react-query to implement fully typed api requests

Apperside 5 Aug 5, 2022
HTML meta tags for React-based apps. Works for both client- and server-side rendering, and has a strict but flexible API.

React Document Meta HTML meta tags for React-based apps. Works for both client- and server-side rendering, and has a strict but flexible API. Built wi

kodyl 320 Nov 18, 2022
React project with Google Firebase API, create partys with admin view and user view. Users can ask topics to admin, and support other topics.

?? Tecnologias Esse projeto foi desenvolvido com as seguintes tecnologias: React Firebase TypeScript ?? Como executar Clone o projeto e acesse a pasta

Felipe Souza 3 Aug 27, 2021
Compares Discord libraries and their support of new API features. Made with React, Next.js, and Bulma.

Compares Discord libraries and their support of new API features. Made with React, Next.js, and Bulma.

Advaith 109 Nov 25, 2022
React implementation of the service locator pattern using Hooks, Context API, and Inversify.

React Service Locator An implementation of the service locator pattern for React 16.13+ using Hooks, Context API, and Inversify. Features Service cont

Carlos González 22 Oct 10, 2022