CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-solid-primitives--utils

A bunch of reactive utility types and functions, for building primitives with Solid.js

Pending
Overview
Eval results
Files

reactive-utilities.mddocs/

Reactive Utilities

Core utilities for working with Solid.js reactivity system, including accessor manipulation, signal creation, and lifecycle management. These functions are designed to integrate seamlessly with Solid.js's reactive patterns and signal-based state management.

Capabilities

MaybeAccessor Access

Functions for working with values that may or may not be accessors (reactive functions).

/**
 * Accesses the value of a MaybeAccessor
 * @param v - Value or accessor function to access
 * @returns The accessed value
 */
function access<T extends MaybeAccessor<any>>(v: T): MaybeAccessorValue<T>;

/**
 * Access an array of MaybeAccessors
 * @param list - Array of values or accessor functions
 * @returns Array of accessed values
 */
function accessArray<A extends MaybeAccessor<any>>(
  list: readonly A[]
): MaybeAccessorValue<A>[];

/**
 * Run the function if the accessed value is not undefined nor null
 * @param value - MaybeAccessor to check and access
 * @param fn - Function to run with the non-null value
 */
function withAccess<T, A extends MaybeAccessor<T>, V = MaybeAccessorValue<A>>(
  value: A,
  fn: (value: NonNullable<V>) => void
): void;

/**
 * Convert MaybeAccessor to Accessor
 * @param v - Value or accessor to convert
 * @returns Accessor function
 */
function asAccessor<A extends MaybeAccessor<unknown>>(
  v: A
): Accessor<MaybeAccessorValue<A>>;

/**
 * If value is a function – call it with given arguments – otherwise get the value as is
 * @param valueOrFn - Value or function to access
 * @param args - Arguments to pass if valueOrFn is a function
 * @returns Accessed value or function result
 */
function accessWith<T>(
  valueOrFn: T,
  ...args: T extends AnyFunction ? Parameters<T> : never
): T extends AnyFunction ? ReturnType<T> : T;

Usage Examples:

import { createSignal } from "solid-js";
import { access, accessArray, withAccess, asAccessor } from "@solid-primitives/utils";

// Basic access
const value = access("hello"); // "hello"
const signalValue = access(() => "world"); // "world"

// Array access
const values = accessArray([1, () => 2, 3]); // [1, 2, 3]

// Conditional access
withAccess(() => "value", (val) => {
  console.log(val); // "value"
});

// Convert to accessor
const accessor = asAccessor("static value");
console.log(accessor()); // "static value"

Signal Creation

Enhanced signal creation functions with special capabilities like hydration support.

/**
 * A hydratable version of createSignal. Uses serverValue on server and update function on client.
 * During hydration uses serverValue as initial and updates once hydration is complete.
 * @param serverValue - Initial value for server
 * @param update - Function called on client to initialize value
 * @param options - Signal options
 * @returns Signal tuple [state, setState]
 */
function createHydratableSignal<T>(
  serverValue: T,
  update: () => T,
  options?: SignalOptions<T>
): ReturnType<typeof createSignal<T>>;

/** @deprecated use createHydratableSignal instead */
const createHydrateSignal: typeof createHydratableSignal;

Usage Examples:

import { createHydratableSignal } from "@solid-primitives/utils";

// Server-side rendering compatible signal
const [theme, setTheme] = createHydratableSignal(
  "light", // Server default
  () => localStorage.getItem("theme") || "light" // Client initialization
);

// With options
const [count, setCount] = createHydratableSignal(
  0,
  () => parseInt(sessionStorage.getItem("count") || "0"),
  { equals: false }
);

Deferred Effects

Advanced effect utilities for handling delayed and conditional reactive updates.

/**
 * Solid's `on` helper, but always defers and returns provided initial value instead of undefined
 * @param deps - Dependencies to watch (accessor or array of accessors)
 * @param fn - Effect function
 * @param initialValue - Initial value to return
 * @returns Effect function
 */
function defer<S, Next extends Prev, Prev = Next>(
  deps: AccessorArray<S> | Accessor<S>,
  fn: (input: S, prevInput: S, prev: undefined | NoInfer<Prev>) => Next,
  initialValue: Next
): EffectFunction<undefined | NoInfer<Next>, NoInfer<Next>>;

function defer<S, Next extends Prev, Prev = Next>(
  deps: AccessorArray<S> | Accessor<S>,
  fn: (input: S, prevInput: S, prev: undefined | NoInfer<Prev>) => Next,
  initialValue?: undefined
): EffectFunction<undefined | NoInfer<Next>>;

Usage Examples:

import { createSignal, createEffect } from "solid-js";
import { defer } from "@solid-primitives/utils";

const [count, setCount] = createSignal(0);

createEffect(defer(
  count,
  (value, prevValue, prev) => {
    console.log(`Count changed from ${prevValue} to ${value}`);
    return value * 2;
  },
  0 // Initial value
));

Lifecycle Utilities

Functions for working with Solid.js lifecycle hooks and cleanup.

/**
 * Solid's onCleanup that doesn't warn in development if used outside of a component
 */
const tryOnCleanup: typeof onCleanup;

Callback Management

Utilities for managing and executing multiple callback functions.

/**
 * Create a callback stack for managing multiple callbacks
 * @returns Object with push, execute, and clear methods
 */
function createCallbackStack<A0 = void, A1 = void, A2 = void, A3 = void>(): {
  push: (...callbacks: ((arg0: A0, arg1: A1, arg2: A2, arg3: A3) => void)[]) => void;
  execute: (arg0: A0, arg1: A1, arg2: A2, arg3: A3) => void;
  clear: VoidFunction;
};

/**
 * Group synchronous function calls into microtasks
 * @param fn - Function to group calls for
 * @returns Grouped function
 */
function createMicrotask<A extends any[] | []>(fn: (...a: A) => void): (...a: A) => void;

Usage Examples:

import { createCallbackStack, createMicrotask } from "@solid-primitives/utils";

// Callback stack
const stack = createCallbackStack<string>();
stack.push(
  (msg) => console.log("First:", msg),
  (msg) => console.log("Second:", msg)
);
stack.execute("Hello"); // Logs both messages and clears stack

// Microtask grouping
const logGrouped = createMicrotask((message: string) => {
  console.log("Grouped:", message);
});

logGrouped("A");
logGrouped("B"); // Only the last call "B" will execute

Array Utilities

Reactive-specific array manipulation and diffing utilities.

/**
 * Handle items removed and added to the array by diffing by reference
 * @param current - New array instance
 * @param prev - Previous array copy  
 * @param handleAdded - Called for every added item
 * @param handleRemoved - Called for every removed item
 */
function handleDiffArray<T>(
  current: readonly T[],
  prev: readonly T[],
  handleAdded: (item: T) => void,
  handleRemoved: (item: T) => void
): void;

Usage Examples:

import { createSignal, createEffect } from "solid-js";
import { handleDiffArray } from "@solid-primitives/utils";

const [items, setItems] = createSignal<string[]>([]);
let prevItems: string[] = [];

createEffect(() => {
  const currentItems = items();
  handleDiffArray(
    currentItems,
    prevItems,
    (item) => console.log("Added:", item),
    (item) => console.log("Removed:", item)
  );
  prevItems = [...currentItems];
});

Types

type MaybeAccessor<T> = T | Accessor<T>;
type MaybeAccessorValue<T extends MaybeAccessor<any>> = T extends () => any ? ReturnType<T> : T;
type OnAccessEffectFunction<S, Prev, Next extends Prev = Prev> = (
  input: AccessReturnTypes<S>,
  prevInput: AccessReturnTypes<S>,
  v: Prev
) => Next;
type AccessReturnTypes<S> = S extends MaybeAccessor<any>[]
  ? { [I in keyof S]: AccessReturnTypes<S[I]>; }
  : MaybeAccessorValue<S>;

Install with Tessl CLI

npx tessl i tessl/npm-solid-primitives--utils

docs

environment-utilities.md

immutable-arrays.md

immutable-objects.md

index.md

math-operations.md

reactive-utilities.md

type-definitions.md

tile.json