spicyhooks
Version:
A collection of spicy React hooks
256 lines (246 loc) • 8.77 kB
TypeScript
interface InViewElement {
id: string;
element: Element | null;
}
interface InViewElementOptions {
options?: IntersectionObserverInit;
baseOn?: 'id' | 'class';
}
/**
* Custom React hook that tracks which element is currently intersecting the viewport
* using the IntersectionObserver API.
*
* It observes all elements in the document that possess a specific attribute (defaulting to 'id').
* When an element enters the viewport according to the specified threshold, the hook updates
* its state to reflect that element. Only the *first* element found to be intersecting
* in any observation callback cycle is considered "in view".
*
* @param {InViewElementOptions} [config={}] - Optional configuration object.
* @param {IntersectionObserverInit} [config.options={ threshold: 0.9 }] - Options to pass directly to the IntersectionObserver constructor (e.g., `threshold`, `root`, `rootMargin`). Defaults to detecting intersection when 90% of the element is visible.
* @param {string} [config.baseOn='id'] - The HTML attribute name used to query for elements to observe. Defaults to 'id'. Any element with this attribute will be observed.
*
* @returns {InViewElement} An object containing the currently "in view" element and its ID.
* - `element`: The DOM element currently intersecting the viewport based on the options, or `null` if none is actively intersecting or initially.
* - `id`: The value of the `id` attribute of the intersecting element, or an empty string (`''`) if no element is intersecting or initially.
*
* @example
*
* const MyComponent = () => {
* const inViewElement = useInViewElement();
* return (
* <section className='space-x-6'>
* {navbarRoutes.map((route, i) => {
* const elementId = route.href.replace('#', '');
* const isInView = inViewElement.id === elementId;
* return (
* <Link
* key={i}
* href={route.href}
* className={cn(
* 'rounded-md px-2 py-1',
* isInView && 'bg-dark',
* 'hover:bg-dark/50'
* )}
* >
* {route.title}
* </Link>
* );
* })}
* </section>
* ); // This example shows how to highlight the active navigation link based on which section is in view
* };
*
*/
declare function useInViewElement({ options, baseOn, }?: InViewElementOptions): InViewElement;
interface ScreenSize {
width: number;
height: number;
}
/**
* A custom React hook that tracks the current inner dimensions (width and height) of the browser window.
*
* It attaches a 'resize' event listener to the window and updates the returned dimensions
* whenever the window size changes. The hook automatically handles setting the initial dimensions
* on mount and cleaning up the event listener on unmount.
*
* @returns {ScreenSize} An object containing the current screen dimensions:
* - `width` (number): The current inner width of the window in pixels (`window.innerWidth`).
* - `height` (number): The current inner height of the window in pixels (`window.innerHeight`).
*
* @example
* function ResponsiveLayout() {
* const { width, height } = useScreenSize();
*
* const isSmallScreen = width < 600;
*
* return (
* <div>
* <p>Current window size: {width}px x {height}px</p>
* <p>{isSmallScreen && "Mobile"}</p>
* </div>
* );
* }
*/
declare function useScreenSize(): ScreenSize;
/**
* A custom React hook for managing a counter state.
*
* Provides the current count and memoized functions to increment, decrement,
* and reset the count to its initial value.
*
* @param {number} [initialValue=0] - The starting value for the counter. Defaults to 0 if not provided.
* @returns {{
* count: number;
* increment: () => void;
* decrement: () => void;
* reset: () => void;
* }} An object containing:
* - `count`: The current state of the counter.
* - `increment`: A memoized function that increases the count by 1.
* - `decrement`: A memoized function that decreases the count by 1.
* - `reset`: A memoized function that resets the count to the `initialValue`.
*
* @example
* const { count, increment, decrement, reset } = useCounter();
*
* @example
* const { count: score, increment: increaseScore, decrement: decreaseScore } = useCounter(100);
*
* @example
* function MyCounterComponent() {
* const { count, increment, decrement, reset } = useCounter(5);
*
* return (
* <section>
* <h2>Counter</h2>
* <p>Current Count: {count}</p>
* <button onClick={increment}>+</button>
* <button onClick={decrement}>-</button>
* <button onClick={reset}>Reset to 5</button>
* </section>
* );
* }
*/
declare function useCounter(initialValue?: number): {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
};
/**
* A custom React hook that returns the value of a variable from the previous render cycle.
*
* This hook is useful for comparing props or state between renders, for example,
* to trigger effects only when a specific value has changed.
*
* It works by storing the current value in a ref after the component renders,
* so on the next render, the ref still holds the value from the previous cycle.
*
* @template T The type of the value being tracked. Can be any type.
*
* @param {T} value The value whose previous instance you want to track.
* This would typically be a prop or state variable.
*
* @returns {T | null} The value from the previous render.
* Returns `null` on the initial render, as there's no "previous" value yet.
*
* @example
* function Counter() {
* const [count, setCount] = useState(0);
*
* const prevCount = usePrevious(count);
*
* return (
* <section>
* <button onClick={() => setCount((prev) => prev + 1)}>+</button>
* <p>prev:{prevCount}</p>
* <p>current: {count}</p>
* </section>
* );
* }
*/
declare function usePrevious<T>(value: T): T | null;
/**
* A custom React hook for managing a boolean (on/off) toggle state.
*
* Provides the current state (`isOn`) and memoized functions to control the state:
* `toggle` (flips the state), `setOn` (sets state to true), and `setOff` (sets state to false).
* Useful for managing UI elements like checkboxes, modals, dropdowns, etc.
*
* @param {boolean} [initialValue=false] - The initial state of the toggle. Defaults to `false` (off).
* @returns {{
* isOn: boolean;
* toggle: () => void;
* setOn: () => void;
* setOff: () => void;
* }} An object containing:
* - `isOn`: The current boolean state (`true` or `false`).
* - `toggle`: A memoized function to invert the state (true -> false, false -> true).
* - `setOn`: A memoized function to explicitly set the state to `true`.
* - `setOff`: A memoized function to explicitly set the state to `false`.
*
* @example
* const { isOn, toggle } = useToggle();
* console.log(isOn); // false
* toggle(); // Sets isOn to true
*
* @example
* const { isOn: isVisible, toggle: toggleVisibility, setOn: show, setOff: hide } = useToggle(true);
* console.log(isVisible); // true
* hide(); // Sets isVisible to false
* show(); // Sets isVisible to true
*
* @example
* function ModalExample() {
* const { isOn: isModalOpen, toggle: toggleModal, setOff: closeModal } = useToggle(false);
*
* return (
* <section>
* <button onClick={toggleModal}>Open Modal</button>
*
* {isModalOpen && (
* <div className="modal-backdrop">
* <div className="modal-content">
* <p>This is the modal content!</p>
* <button onClick={closeModal}>Close</button> {* Using setOff *}
* </div>
* </div>
* )}
* </section>
* );
* }
*/
declare function useToggle(initialValue?: boolean): {
isOn: boolean;
toggle: () => void;
setOn: () => void;
setOff: () => void;
};
/**
* A custom React hook that debounces a value.
*
* This hook delays updating the value until a specified time has elapsed since the last change.
* Useful for delaying expensive operations like API calls based on rapidly changing input (e.g., search box).
*
* @template T The type of the value being debounced.
* @param {T} value The value to debounce.
* @param {number} delay The delay in milliseconds before the debounced value is updated.
* @returns {T} The debounced value.
*
* @example
* function CounterComponent() {
* const [count, setCount] = useState(0);
*
* const debounceCount = useDebounce(count, 3000);
*
* return (
* <section>
* <button onClick={() => setCount((prev) => prev + 1)}>+</button>
* <p>Instant Count: {count}</p>
* <p>Debounced Count: {debounceCount}</p>
* </section>
* );
* }
*/
declare function useDebounce<T>(value: T, delay: number): T;
export { useCounter, useDebounce, useInViewElement, usePrevious, useScreenSize, useToggle };