All files / react-classic/src binding.ts

100% Statements 35/35
100% Branches 14/14
100% Functions 1/1
100% Lines 35/35

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 55 56 57 58 59 60 611x 1x 1x 1x 1x                                         1x 6x 6x 6x   6x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x   6x 6x   6x 6x   6x 6x 4x 6x 6x 6x   6x 6x  
import { binding, captureStack, type ObjLike } from '@anchorlib/core';
import { useMicrotask } from './hooks.js';
import { CLEANUP_DEBOUNCE_TIME } from './constant.js';
import { useEffect } from 'react';
import { getRefState } from './ref.js';
import type { BindingLink, BindingParam } from './types.js';
 
/**
 * A React hook that creates a binding between a property of a reactive object and another value.
 *
 * This hook establishes a connection between a property in a reactive state object and a binding source,
 * allowing automatic synchronization of values. The binding is automatically cleaned up when the component
 * unmounts or when the dependencies change.
 *
 * @template S - The type of the reactive state object
 * @template T - The type of the binding source object
 * @template B - The type of the key in the binding source object
 *
 * @param state - The reactive state object whose property will be bound
 * @param key - The key of the property in the state object to bind
 * @param bind - A tuple containing the binding source object and key, or undefined to skip binding
 *
 * @returns The same state object with the specified property now bound to the provided source
 * @throws {Error} When attempting to bind to a constant value, as constants cannot be modified after creation
 */
export function useBinding<S extends ObjLike, T, B>(state: S, key: keyof S, bind?: BindingParam<T, B>): S {
  const [cleanup, cancelCleanup] = useMicrotask(CLEANUP_DEBOUNCE_TIME);
  const [bindObj, bindKey] = (Array.isArray(bind) ? bind : [bind]) as BindingLink<T, B>;
  const bindRef = getRefState(bindObj);
 
  if (bindRef?.constant) {
    const error = new Error('Binding to constant is not allowed.');
    captureStack.violation.general(
      'Binding violation detected:',
      'Attempted to bind to a constant.',
      error,
      [
        'Constant value cannot be changed after created.',
        '- Constant only updated when its dependency changed.',
        '- Use variable if you need to update its value later.',
      ],
      useBinding
    );
  }
 
  const unbind =
    bindRef && !bindRef.constant ? binding((bindKey ? [bindObj, bindKey] : bindRef) as never, state, key) : undefined;
 
  useEffect(() => {
    cancelCleanup();
 
    return () => {
      cleanup(() => {
        unbind?.();
      });
    };
  }, [unbind]);
 
  return state;
}