Skip to main content
Version: v1.0

React Hooks

Hooks for connecting reactive values to React components. These are client-side only and must be imported from @dxbox/use-less-react/client.

useReactiveValues

Subscribe to single or multiple reactive value instances (or any AbstractReactiveValue).

Signature

function useReactiveValues<
TReactiveValues extends Record<string, AbstractReactiveValue<any>>,
TResult = ExtractReactiveValues<TReactiveValues>,
>(
reactiveValues: TReactiveValues,
transform?: (values: TReactiveValues) => TResult,
): TResult;

Example

const count = new ReactiveValue(0);
const name = new ReactiveValue("John");

function Component() {
// default: return count and name values as-is
const { count, name } = useReactiveValues({ count, name })
return <div>{count} - {name}</div>;
}

function ComponentWithTransformation() {
const { countPlusOne } = useReactiveValues(
{ count, name },
// transformation function
({ count, name }) => ({
countPlusOne: count.get() + 1,
name
})
);

return <div>{countPlusOne} - {name}</div>;
}

useReactiveStoreValues

Subscribe to specific keys from a ReactiveStore.

Signature

function useReactiveStoreValues<
TStore extends ReactiveStoreType<TStore>,
K extends readonly (keyof TStore)[],
TResult = ExtractReactiveValues<Pick<TStore, K[number]>>,
>(
store: ReactiveStore<TStore>,
keys: K,
transform?: (values: Pick<TStore, K[number]>) => TResult,
): TResult;

Example

function Component() {
const { count, name } = useReactiveStoreValues(store, ["count", "name"]);
return <div>{count} - {name}</div>;
}

function ComponentWithTransformation() {
// With transformation
const { countPlusOne, name } = useReactiveStoreValues(
store,
["count", "name"],
({ count, name }) => ({ countPlusOne: count.get() + 1, name })
);

return <div>{countPlusOne} - {name}</div>;
}

useDisposable

Creates a disposable instance and automatically disposes it when the component unmounts.

Signature

function useDisposable<T extends Disposable>(getDisposable: () => T): T;

The factory function is called only once (on first render) and the instance persists across re-renders. The instance is automatically disposed when the component (or Provider) unmounts.

Important: The [Symbol.dispose]() method should be idempotent (safe to call multiple times), as React StrictMode may call it multiple times in development.

Example

import { useDisposable } from '@dxbox/use-less-react/client';
import { AutoDispose } from '@dxbox/use-less-react/classes';

@AutoDispose
class CounterManager implements Disposable {
store = new ReactiveStore({ count: new ReactiveValue(0) });
// store is automatically detected and disposed (has Symbol.dispose)

[Symbol.dispose](): void {
// Custom cleanup logic if needed
// @AutoDispose automatically disposes store after this
}

increment() {
this.store.values.count.set(c => c + 1);
}
}

function Component() {
// Automatically disposed when component unmounts
const manager = useDisposable(() => new CounterManager());
const { count } = useReactiveStoreValues(manager.store, ['count']);

return (
<div>
{count}
<button onClick={() => manager.increment()}>+</button>
</div>
);
}

Context-provided instance

function AppProvider({ children }) {
// Automatically disposed when Provider unmounts
const store = useDisposable(() => new ReactiveStore({...}));

return (
<StoreProvider value={store}>
{children}
</StoreProvider>
);
}

Sharing instances

To create and share instances across components, use useDisposable where you instantiate the instance, and createGenericContext to share instances without prop-drilling:

import { useDisposable } from "@dxbox/use-less-react/client";
import { AutoDispose } from "@dxbox/use-less-react/classes";

@AutoDispose
class CounterManager implements Disposable {
store = new ReactiveStore({ count: new ReactiveValue(0) });
// store is automatically detected and disposed

[Symbol.dispose](): void {
// Custom cleanup logic if needed
}
}

function Component() {
const manager = useDisposable(() => new CounterManager());
// Automatically disposed on unmount
}

Shared instance (Context)

import { createGenericContext } from '@dxbox/use-less-react/client';
import { useDisposable } from '@dxbox/use-less-react/client';

const [ManagerProvider, useManager] = createGenericContext<CounterManager>();

function App() {
const manager = useDisposable(() => new CounterManager());

return (
<ManagerProvider value={manager}>
<ChildComponent />
</ManagerProvider>
);
}

function ChildComponent() {
const manager = useManager();
const { count } = useReactiveValues({ count: manager.count });

return (
<div>
{count}
<button onClick={() => manager.increment()}>+</button>
</div>
);
}