All files / src/primitives memo.ts

100% Statements 9/9
100% Branches 2/2
100% Functions 1/1
100% Lines 9/9

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 551x 1x                                                                       1x     65x       65x 119x 65x           65x 65x  
import { createSignal } from './signal';
import { createEffect } from './effect';
import type { Memo, ReactiveOptions } from '../types';
 
/**
 * Creates a derived, memoized signal that caches its computed value.
 *
 * Memos are read-only signals that compute their value from other signals.
 * They automatically track dependencies and only re-compute when those dependencies change.
 * The computed value is cached and returned on subsequent reads until dependencies change.
 *
 * @param fn The function to compute the memoized value - runs when dependencies change
 * @param options Optional configuration including debug name
 * @returns A read-only memo that can be called to get the current computed value
 *
 * @example
 * ```typescript
 * const [firstName, setFirstName] = createSignal('John');
 * const [lastName, setLastName] = createSignal('Doe');
 *
 * const fullName = createMemo(() => `${firstName()} ${lastName()}`);
 *
 * console.log(fullName()); // "John Doe"
 * setFirstName('Jane');    // Triggers re-computation
 * console.log(fullName()); // "Jane Doe"
 *
 * // Memos are lazy - they only compute when read
 * const expensive = createMemo(() => {
 *   console.log('Computing...');
 *   return heavyComputation();
 * });
 *
 * // Nothing computed yet
 * expensive(); // Logs "Computing..." and returns result
 * expensive(); // Returns cached result (no re-computation)
 * ```
 */
export function createMemo<T>(fn: () => T, options?: ReactiveOptions): Memo<T> {
  // A memo is essentially a signal that is updated by an effect.
  // The signal can hold either the computed value T or be undefined initially.
  const [memo, setMemo] = createSignal<T | undefined>(undefined, options);
 
  // This effect tracks the dependencies of the memo function and updates the signal's value.
  // Pass the name to the underlying effect for better debugging
  createEffect(() => {
    setMemo(fn());
  }, options);
 
  // The type cast here is safe and intentional.
  // The internal effect runs synchronously upon creation, so the `memo` signal
  // is guaranteed to have a value of type `T` before it's returned to the user.
  // This hides the initial `undefined` state from the public API.
  return memo as Memo<T>;
}