React Adapter

The @banksia/signals/react subpath provides hooks and Higher-Order Components (HOCs) to seamlessly integrate @banksia/signals into React 18 and React 19 applications.


Installation

pnpm add @banksia/signals react react-dom

useReactive(store)

Subscribes the host component to changes within a reactive object, collection, signal, or domain store instance:

import React from "react";
import { useReactive } from "@banksia/signals/react";
import { cartStore } from "./stores/cart-store";

export const CartSummary: React.FC = () => {
  // Component re-renders whenever cartStore properties mutate:
  const cart = useReactive(cartStore);

  return (
    <div className="cart-card">
      <h3>Items in cart: {cart.items.length}</h3>
      <p>Total: ${cart.total.toFixed(2)}</p>
      <button
        onClick={() =>
          cart.addItem({
            id: "sku-1",
            name: "Notebook",
            price: 12.5,
            quantity: 1,
          })
        }
      >
        Add Item
      </button>
    </div>
  );
};

observer(Component)

Transforms any functional component into an auto-tracking reactive observer. Any signal, computed, or reactive proxy property read during render automatically becomes a tracked dependency:

import React from "react";
import { observer } from "@banksia/signals/react";
import { userSession } from "./stores/user-session";

export const UserBadge = observer(() => {
  return (
    <div className="badge">
      <span>{userSession.user.name}</span>
      {userSession.isAdmin && <span className="admin-tag">Admin</span>}
    </div>
  );
});

useSignal(initialValue)

Creates and memoizes a component-local Signal tied to the lifecycle of the host React component:

import React from "react";
import { useSignal } from "@banksia/signals/react";

export const LocalCounter: React.FC = () => {
  const count = useSignal(0);

  return <button onClick={() => count.value++}>Count: {count.value}</button>;
};

useComputed(fn)

Creates and memoizes a derived computed() computation within a React component:

import React from "react";
import { useSignal, useComputed } from "@banksia/signals/react";

export const MathWidget: React.FC = () => {
  const count = useSignal(2);
  const squared = useComputed(() => count.value * count.value);

  return (
    <div>
      <p>Base: {count.value}</p>
      <p>Squared: {squared.value}</p>
      <button onClick={() => count.value++}>Increment</button>
    </div>
  );
};

TypeScript Signatures

export function useReactive<T extends object>(target: T): T;
export function observer<P extends object>(
  Component: React.ComponentType<P>,
): React.FC<P>;
export function useSignal<T>(initialValue: T): Signal<T>;
export function useComputed<T>(fn: () => T): ReadonlySignal<T>;