rect.sh

React

RectProvider and the hooks.

@rectsh/rect/react binds the store to React with useSyncExternalStore, so reads are concurrent-safe and components re-render only when their slice changes.

Provider

Wrap your view in RectProvider. It connects to the host and gates children until ready, so the hooks below never see a half-connected store.

import { RectProvider } from '@rectsh/rect/react';

<RectProvider
  fallback={<p>Connecting…</p>}
  errorFallback={(err) => <p>{err.message}</p>}
>
  <View />
</RectProvider>;

Pass store to inject a store directly (see Testing).

Reading

import { useRectState, useRectValue } from '@rectsh/rect/react';

// The whole view model — re-renders on any change.
const state = useRectState<Note>();

// A derived slice — re-renders only when it changes (Object.is, or your isEqual).
const count = useRectValue<Note, number>((s) => s.items.length);

Writing

import { useRectField, useRectActions } from '@rectsh/rect/react';

// Two-way binding for one path — great for inputs.
function TitleInput() {
  const [title, setTitle] = useRectField<string>('title');
  return (
    <input value={title ?? ''} onChange={(e) => setTitle(e.target.value)} />
  );
}

// Stable action handles for callbacks.
function Approve() {
  const { patch } = useRectActions();
  return <button onClick={() => patch({ approved: true })}>Approve</button>;
}

Dispatching actions

When the view defines named actions, useRectDispatch() runs them — the handler executes host-side, and a business rejection comes back as a value, not an exception:

import { useRectDispatch } from '@rectsh/rect/react';

function Approve() {
  const dispatch = useRectDispatch();

  return (
    <button
      onClick={async () => {
        const result = await dispatch('approve');
        if (!result.ok) showToast(result.message ?? result.code);
      }}
    >
      Approve
    </button>
  );
}

Everything

HookReturns
useRectState<T>()The whole view model (subscribed).
useRectValue(selector, isEqual?)A derived slice (subscribed).
useRectField<V>(path)[value, setValue] for one path.
useRectActions<T>()Stable { set, update, patch }.
useRectDispatch()(action, input?) => Promise<RectDispatchResult>.
useRect()The raw store (escape hatch).
useRectRevision()The current revision (subscribed).
useRectMeta()The static host metadata.

Full signatures are in the generated API reference.

On this page