CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-rc-util

Common utility functions and components specifically designed for React development.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

react-hooks.mddocs/

React Hooks

Modern React hooks for state management, memoization, and lifecycle optimization with enhanced functionality beyond standard React hooks.

Capabilities

useMergedState (useControlledState)

Hook for managing controlled/uncontrolled state patterns, commonly used in form components that can be either controlled or uncontrolled.

/**
 * Hook for controlled/uncontrolled state management
 * @param {T | (() => T)} defaultStateValue - Default state value or function
 * @param {object} [option] - Configuration options
 * @returns {[R, (value: T) => void]} State value and setter function
 */
function useControlledState<T, R = T>(
  defaultStateValue: T | (() => T),
  option?: {
    /** Default value when component is uncontrolled */
    defaultValue?: T | (() => T);
    /** External value when component is controlled */
    value?: T;
    /** Callback when value changes */
    onChange?: (value: T, prevValue: T) => void;
    /** Transform state value before returning */
    postState?: (value: T) => T;
  }
): [R, (value: T) => void];

Usage Examples:

import useControlledState from 'rc-util/lib/hooks/useMergedState';

// Basic controlled/uncontrolled input
function Input({ value, defaultValue, onChange }) {
  const [inputValue, setInputValue] = useControlledState('', {
    value,
    defaultValue,
    onChange
  });

  return (
    <input
      value={inputValue}
      onChange={(e) => setInputValue(e.target.value)}
    />
  );
}

// With post-processing
function NumberInput({ value, onChange }) {
  const [numValue, setNumValue] = useControlledState(0, {
    value,
    onChange,
    postState: (val) => Math.max(0, val) // Ensure non-negative
  });

  return (
    <input
      type="number"
      value={numValue}
      onChange={(e) => setNumValue(Number(e.target.value))}
    />
  );
}

// Uncontrolled with default
<Input defaultValue="Hello" />

// Controlled
<Input value={inputValue} onChange={setInputValue} />

useEffect

Enhanced useEffect that passes previous dependencies to the callback, useful for comparison logic.

/**
 * Enhanced useEffect that passes previous dependencies to callback
 * @param {(prevDeps: any[]) => void} callback - Effect callback receiving previous deps
 * @param {any[]} deps - Dependencies array
 */
function useEffect(
  callback: (prevDeps: any[]) => void,
  deps: any[]
): void;

Usage Example:

import useEffect from 'rc-util/lib/hooks/useEffect';

function Component({ userId, filterType }) {
  useEffect((prevDeps) => {
    const [prevUserId, prevFilterType] = prevDeps;
    
    // Only refetch if userId changed, not filterType
    if (prevUserId !== userId) {
      fetchUserData(userId);
    }
    
    // Only update filter if filterType changed
    if (prevFilterType !== filterType) {
      updateFilter(filterType);
    }
  }, [userId, filterType]);
}

useMemo

Custom memoization hook with custom comparison function for more control over when values are recalculated.

/**
 * Custom memoization hook with custom comparison function
 * @param {() => Value} getValue - Function to get the memoized value
 * @param {Condition} condition - Current condition for comparison
 * @param {(prev: Condition, next: Condition) => boolean} shouldUpdate - Comparison function
 * @returns {Value} Memoized value
 */
function useMemo<Value, Condition = any[]>(
  getValue: () => Value,
  condition: Condition,
  shouldUpdate: (prev: Condition, next: Condition) => boolean
): Value;

Usage Examples:

import useMemo from 'rc-util/lib/hooks/useMemo';

// Deep comparison for objects
function ExpensiveComponent({ config }) {
  const processedData = useMemo(
    () => expensiveCalculation(config),
    config,
    (prev, next) => JSON.stringify(prev) !== JSON.stringify(next)
  );

  return <div>{processedData}</div>;
}

// Custom comparison for arrays
function ListComponent({ items, sortBy }) {
  const sortedItems = useMemo(
    () => [...items].sort((a, b) => a[sortBy] - b[sortBy]),
    { items, sortBy },
    (prev, next) => 
      prev.items.length !== next.items.length || 
      prev.sortBy !== next.sortBy ||
      prev.items.some((item, index) => item.id !== next.items[index]?.id)
  );

  return (
    <ul>
      {sortedItems.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
  );
}

Reference Utilities

ref

React ref manipulation utilities for working with refs in complex scenarios.

/**
 * Fill a ref with a node value
 * @param {React.Ref<T>} ref - Ref to fill (function or object ref)
 * @param {T} node - Node value to assign
 */
function fillRef<T>(ref: React.Ref<T>, node: T): void;

/**
 * Merge multiple refs into one ref function
 * @param {...React.Ref<T>[]} refs - Refs to merge
 * @returns {React.Ref<T>} Combined ref function
 */
function composeRef<T>(...refs: React.Ref<T>[]): React.Ref<T>;

/**
 * Check if a component supports refs
 * @param {any} nodeOrComponent - React component or node
 * @returns {boolean} True if component supports refs
 */
function supportRef(nodeOrComponent: any): boolean;

Usage Examples:

import { fillRef, composeRef, supportRef } from 'rc-util/lib/ref';

// Compose multiple refs
function ForwardedComponent(props, ref) {
  const internalRef = useRef();
  const combinedRef = composeRef(ref, internalRef);

  useEffect(() => {
    // Use internal ref for component logic
    if (internalRef.current) {
      internalRef.current.focus();
    }
  }, []);

  return <input ref={combinedRef} />;
}

// Check ref support before using
function ParentComponent({ children }) {
  const childRef = useRef();

  const enhancedChildren = React.Children.map(children, (child) => {
    if (supportRef(child)) {
      return React.cloneElement(child, { ref: childRef });
    }
    return child;
  });

  return <div>{enhancedChildren}</div>;
}

// Manual ref filling
function CustomRef() {
  const handleRef = useCallback((node) => {
    // Fill multiple refs manually
    fillRef(externalRef, node);
    fillRef(internalRef, node);
  }, []);

  return <div ref={handleRef} />;
}

Animation & Performance

raf

RequestAnimationFrame wrapper with fallback for server-side rendering and older browsers.

/**
 * RequestAnimationFrame wrapper with fallback
 * @param {() => void} callback - Function to call on next frame
 * @returns {number} Request ID for cancellation
 */
function wrapperRaf(callback: () => void): number;

/**
 * Cancel a requestAnimationFrame request
 * @param {number} id - Request ID to cancel
 */
wrapperRaf.cancel: (id: number) => void;

Usage Example:

import raf from 'rc-util/lib/raf';

// Schedule animation
const rafId = raf(() => {
  // Animation code here
  element.style.opacity = '1';
});

// Cancel if needed
raf.cancel(rafId);

// Use in custom hook
function useAnimationFrame(callback) {
  const requestRef = useRef();

  useEffect(() => {
    const animate = () => {
      callback();
      requestRef.current = raf(animate);
    };
    requestRef.current = raf(animate);

    return () => {
      if (requestRef.current) {
        raf.cancel(requestRef.current);
      }
    };
  }, [callback]);
}

Install with Tessl CLI

npx tessl i tessl/npm-rc-util

docs

core-utilities.md

development-styling.md

dom-utilities.md

index.md

react-components.md

react-hooks.md

tile.json