@winglet/react-utils
Version:
React utility library providing custom hooks, higher-order components (HOCs), and utility functions to enhance React application development with improved reusability and functionality
71 lines (70 loc) • 2.89 kB
TypeScript
import { type DependencyList } from 'react';
/**
* Executes a layout effect synchronously until a specified condition is met, then stops permanently.
*
* This hook is the synchronous version of `useEffectUntil`, using `useLayoutEffect` to run
* before the browser paints. It's ideal for DOM measurements and manipulations that must
* complete before the user sees the screen update.
*
* ### When to Use Over useEffectUntil
* - **DOM Measurements**: When you need accurate layout measurements before paint
* - **Style Calculations**: Computing styles based on element dimensions
* - **Scroll Position Management**: Restoring scroll positions without visual jumps
* - **Animation Setup**: Initializing animation states to prevent flicker
* - **Focus Management**: Setting focus without visual delays
*
* ### Behavior
* - Runs synchronously after DOM mutations but before browser paint
* - Blocks browser painting until completion (use sparingly)
* - Once the effect returns `true`, it permanently stops executing
* - The completion state persists across re-renders
*
* ### Performance Considerations
* Since this runs synchronously and blocks painting, it can impact performance.
* Only use when the synchronous behavior is necessary to prevent visual issues.
*
* @example
* ```typescript
* // Measure and adjust layout until it fits
* useLayoutEffectUntil(() => {
* const element = ref.current;
* if (!element) return false;
*
* const { width } = element.getBoundingClientRect();
* if (width > maxWidth) {
* element.style.fontSize = `${currentSize - 1}px`;
* setCurrentSize(prev => prev - 1);
* return false; // Keep adjusting
* }
* return true; // Fits perfectly
* }, [currentSize, maxWidth]);
*
* // Restore scroll position without flicker
* useLayoutEffectUntil(() => {
* const savedPosition = sessionStorage.getItem('scrollPos');
* if (!savedPosition) return true;
*
* window.scrollTo(0, parseInt(savedPosition));
* sessionStorage.removeItem('scrollPos');
* return true; // Done restoring
* }, []);
*
* // Initialize animation state before first paint
* useLayoutEffectUntil(() => {
* const elements = document.querySelectorAll('.animate');
* if (elements.length === 0) return false;
*
* elements.forEach(el => {
* el.style.opacity = '0';
* el.style.transform = 'translateY(20px)';
* });
* setAnimationReady(true);
* return true; // Initialization complete
* }, []);
* ```
*
* @typeParam Dependencies - The type of the dependency array
* @param effect - A function that performs synchronous side effects and returns `true` when done
* @param dependencies - Optional dependency array that triggers re-execution when changed
*/
export declare const useLayoutEffectUntil: <Dependencies extends DependencyList>(effect: () => boolean, dependencies?: Dependencies) => void;