reactive logo

Reactive Values

The count signal value is

The doubleCount computed value is


  const count = signalValue(0);
  const doubleCount = computedValue(() => count() * 2, [count]);

  count.effect((val) => {
    const htmlElement = document.querySelector('#reactiveCounter')!;
    htmlElement.innerHTML = `${val}`;
  });

  doubleCount.effect((val) => {
    const htmlElement = document.querySelector('#computedCounter')!;
    htmlElement.innerHTML = `${val}`;
  });

What is Reactive Values?

A lightweight and extensible JavaScript & TypeScript library for managing reactive values and computed dependencies. Designed for projects that need reactivity without relying on heavy frameworks.

Installation


  npm install reactive-values

What's Included?

Module Description
Listener<T> A function that reacts to value changes
SignalValue<T> Interface for signal values with set, and effect methods
ComputedValue<T> Interface for computed values with effect method
signalValue(initialValue, options?) Creates a reactive value with deep equality, batching, and listener support
computedValue(compute, deps, options?) Creates a derived value that updates automatically when dependencies change
deepEqual(a, b) Performs deep equality checks between complex values

Core Concepts

Listener<T>


  type Listener<T> = (value: T) => void;

A function that runs whenever the reactive value changes. Useful for syncing UI, triggering side effects, or propagating state.

SignalValue<T>


  interface SignalValue<T> {
    () => T;
    set: (value: T) => void;
    effect: (listener: (value: T) => void) => (() => boolean);
  }

  • (): Returns the current value.
  • .set(value): Updates the value and notifies listeners if it changed.
  • .effect(listener): Registers a listener and immediately invokes it. Returns a function to remove the listener.

Options:


  signalValue(initialValue, { asyncEffect: true });
  signalValue(initialValue, { asyncUpdates: true });
  signalValue(initialValue, { asyncEffect: true, asyncUpdates: true });

ComputedValue<T>


  interface ComputedValue<T> {
    () => T;
    effect: (listener: (value: T) => void) => (() => boolean);
  }

  • (): Returns the current value.
  • .effect(listener): Registers a listener and immediately invokes it. Returns a function to remove the listener.

Options:


  computedValue(initialValue, { asyncEffect: true });
  computedValue(initialValue, { asyncUpdates: true });
  computedValue(initialValue, { asyncEffect: true, asyncUpdates: true });

How to use

signalValue(initialValue, options?)


  import { signalValue } from 'reactive-values';
  
  const count = signalValue(0, { asyncEffect: false, asyncUpdates: true });
  
  count.effect(val => console.log('Count:', val));
  count.set(1);
  count();

computedValue(compute, deps, options?)


  import { signalValue, computedValue } from 'reactive-values';
  
  const firstName = signalValue('Jhon');
  const lastName = signalValue('Doe');
  
  const fullName = computedValue(
    () => `${firstName.get()} ${lastName.get()}`,
    [firstName, lastName],
    { asyncEffect: false, asyncUpdates: true }
  );

 fullName.effect((name) => console.log('Full name:', name));
 fullName(); // Show the value

deepEqual(a, b)


  import { deepEqual } from 'reactive-values';
  
  const isEqual = deepEqual({ a: 1 }, { a: 1 }); // true

Full Example


  import { signalValue, computedValue } from 'reactive-values';

  const price = signalValue(100);
  const quantity = signalValue(2);

  const total = computedValue(() => price.get() * quantity.get(), [price, quantity], {
    asyncEffect: false,
    asyncUpdates: true
  });

  total.effect(val => console.log('Updated total:', val));

  price.set(120);  // "Updated total: 240" (batched async)
  quantity.set(3); // "Updated total: 360" (batched async)

Purpose

Reactive Values provides a clean foundation for building reactive systems in TypeScript. It emphasizes simplicity, modularity, and flexibility—perfect for custom UI logic, state management, or animation triggers.

Useful links