@denovo-hellas/common
v1.2.4
Published
Common functions and hooks
Readme
useArray Hook
The useArray hook provides a set of utility functions to manage and manipulate an array state in a React component. It leverages the useState hook to maintain the state of the array and offers methods for common operations like adding, filtering, updating, and removing elements from the array.
TypeScript Definition
function useArray<T>(defaultValue: T[]): {
data: T[];
set: React.Dispatch<React.SetStateAction<T[]>>;
push: (element: T) => void;
filter: (callback: (element: T, index: number, array: T[]) => boolean) => void;
update: (index: number, newElement: T) => void;
remove: (index: number) => void;
clear: () => void;
updateAll: (callback: (element: T, index: number, array: T[]) => T) => void;
}Usage
import useArray from './useArray';
function MyComponent() {
const { data, push, remove, update, clear, filter, updateAll } = useArray<number>([]);
// Example usage:
push(5); // Adds 5 to the array
update(0, 10); // Updates the first element to 10
remove(0); // Removes the first element
clear(); // Clears the entire array
filter((element) => element > 10); // Keeps elements greater than 10
updateAll((element) => element * 2); // Doubles every element in the array
return (
<div>
{data.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);
}Parameters
defaultValue: T[]: The initial value of the array. It sets up the initial state of the array.
Returns
The useArray hook returns an object containing the following properties and methods:
Properties
data: T[]: The current state of the array.set: React.Dispatch<React.SetStateAction<T[]>>: A setter function to directly update the state of the array, similar touseState.
Methods
push(element: T): void- Adds a new element to the end of the array.
- Parameters:
element- The element to add to the array.
filter(callback: (element: T, index: number, array: T[]) => boolean): void- Filters the array based on the provided callback function, keeping only the elements for which the callback returns
true. - Parameters:
callback- A function that receives each element, its index, and the entire array, and returns a boolean indicating whether the element should remain in the array.
- Filters the array based on the provided callback function, keeping only the elements for which the callback returns
update(index: number, newElement: T): void- Updates the element at the specified index with a new element.
- Parameters:
index- The index of the element to update.newElement- The new element to replace the existing one.
remove(index: number): void- Removes the element at the specified index from the array.
- Parameters:
index- The index of the element to remove.
clear(): void- Clears the entire array, setting it to an empty array.
updateAll(callback: (element: T, index: number, array: T[]) => T): void- Updates all elements in the array using the provided callback function.
- Parameters:
callback- A function that receives each element, its index, and the entire array, and returns a new value for each element.
Example Use Cases
- Managing a dynamic list of items, such as a to-do list where you can add, remove, and update tasks.
- Filtering or transforming data, for example, filtering out specific values or modifying all elements with a given transformation.
Notes
- This hook makes managing arrays in React state more convenient by abstracting common operations into easy-to-use functions.
- The
useArrayhook can be used with any typeT, making it a generic and reusable utility for managing arrays of different types (e.g., strings, numbers, objects).
useAsyncOnce Hook
Function Signature
export function useAsyncOnce<T extends (...args: any[]) => Promise<any>>(asyncFunction: T): UseAsyncOnceProps<T> { ... }Parameters
asyncFunction: An asynchronous function of typeTthat you want to execute. This function can take any number of arguments and must return aPromise.
Returns
An object of type UseAsyncOnceProps<T> containing:
call: A function that invokesasyncFunction. This function accepts the same parameters asasyncFunction.isExecuting: A boolean value that indicates whetherasyncFunctionis currently being executed.
Usage Example
import React from 'react';
import { useAsyncOnce } from '@/hooks/useAsyncOnce';
async function fetchData() {
// Simulating an async data fetch
return await fetch('https://api.example.com/data');
}
function MyComponent() {
const { call, isExecuting } = useAsyncOnce(fetchData);
return (
<div>
<button onClick={call} disabled={isExecuting}>
{isExecuting ? 'Loading...' : 'Fetch Data'}
</button>
</div>
);
}Detailed Explanation
State Management
isExecuting: A boolean state, managed usinguseState, that tracks whether theasyncFunctionis currently being executed. It is initialized tofalse.
call Function
useCallbackis used to memoize thecallfunction, ensuring that it only changes whenasyncFunctionorisExecutingchanges.- The
callfunction performs the following:- Checks if
isExecutingistrue. If it is, the function returns early, preventing multiple simultaneous executions. - Sets
isExecutingtotruebefore callingasyncFunction. - Uses a
try-catch-finallyblock to:- Await the execution of
asyncFunction. - Log an error to the console if the call fails.
- Reset
isExecutingtofalsewhen the function completes, regardless of success or failure.
- Await the execution of
- Checks if
Benefits
- Prevents multiple simultaneous calls: By using
isExecuting, the hook ensures thatasyncFunctionisn't called again until the previous execution finishes. - Handles state management: Automatically updates
isExecutingto inform your components when the function is running.
useDebounce Hook
The useDebounce hook is a utility for debouncing a callback function, which limits the rate at which the function is invoked. This is useful in scenarios where frequent events (such as typing or scrolling) would otherwise cause a function to run too many times. The hook ensures that the callback is only called after the specified delay has elapsed since the last time the hook dependencies were updated.
Function Signature
export default function useDebounce({ callback, delay, dependencies }: UseDebounceProps): voidParameters
callback:Function
The function to be debounced. This is the function that will be called after the specifieddelayif no dependencies change during that time.delay:number
The delay in milliseconds that the hook will wait after the last change in dependencies before callingcallback.dependencies:Array<any>
An array of dependencies that, when changed, will reset the debounce timer. This is similar to the dependency array inuseEffect.
Return Value
The useDebounce hook does not return a value.
How It Works
useTimeoutis used internally to set up a timer. This timer is configured to trigger thecallbackfunction after thedelay.resetandclearare utility functions provided byuseTimeout:resetrestarts the timer every time there is a change in any of thedependencies.clearstops the timer when the component is unmounted.
Example Usage
import useDebounce from './useDebounce';
function SearchComponent({ searchTerm }) {
const debouncedSearch = () => {
// Perform search logic here, e.g., API call
console.log('Searching:', searchTerm);
};
// Set up debounce with a 500ms delay
useDebounce({
callback: debouncedSearch,
delay: 500,
dependencies: [searchTerm],
});
return <div>{/* Your component UI */}</div>;
}In this example:
debouncedSearchis only called 500ms after the lastsearchTermupdate.- If
searchTermchanges before 500ms have passed, the timer resets.
useTimeout Hook
The useTimeout hook provides a utility for managing a timeout in React. It allows you to set a timeout that will execute a callback function after a specified delay and offers methods to reset or clear the timeout.
Function Signature
export default function useTimeout({ callback, delay }: UseTimeoutProps): { reset: () => void; clear: () => void }Parameters
callback:Function
The function to be executed after the specifieddelay. This callback is invoked only once per timeout interval, or each timeresetis called.delay:number
The amount of time, in milliseconds, to wait before calling thecallbackfunction.
Return Value
The useTimeout hook returns an object with the following methods:
reset:Function
A function to reset the timeout. This will clear the existing timeout (if any) and start a new one based on thedelayparameter.clear:Function
A function to clear the timeout. This stops the timeout from executing thecallbackfunction if it hasn’t already executed.
How It Works
Internally, the hook manages a timeout using the following techniques:
useRefis used to store references to thecallbackandtimeout.useCallbackmemoizes theset,clear, andresetfunctions to ensure that they are stable across renders.useEffect:- Updates
callbackRefwhenevercallbackchanges, ensuring the latest function is called even if the timeout is still active. - Automatically sets the timeout when the component mounts and clears it on unmount or if
delaychanges.
- Updates
Example Usage
import useTimeout from './useTimeout';
function Notification({ message, duration }) {
const { reset, clear } = useTimeout({
callback: () => {
alert('Timeout expired!');
},
delay: duration,
});
return (
<div>
<p>{message}</p>
<button onClick={reset}>Reset Timeout</button>
<button onClick={clear}>Clear Timeout</button>
</div>
);
}In this example:
- A timeout is set to show an alert after
durationmilliseconds. - The "Reset Timeout" button restarts the timeout when clicked, while the "Clear Timeout" button stops the timeout, preventing the alert from being displayed.
useEventListener Hook
The useEventListener hook is a custom React hook that simplifies attaching and managing event listeners on a specified element. It supports dynamically updating the callback function and ensures cleanup on unmount, making it especially useful for handling DOM events in React.
Function Signature
export function useEventListener({ eventType, callback, element }: UseEventListenerProps): voidParameters
eventType:string
The type of event to listen for (e.g.,"click","scroll","resize").callback:Function
The function to be executed when the event is triggered. The callback receives the event object as its argument.element:RefObject<HTMLElement> | Window | null
The target element to which the event listener will be attached. This can be a Reactreffor a DOM element,window, ornull(in which case no event listener is attached).
Return Value
This hook does not return any value.
How It Works
callbackRefis auseRefhook that holds a reference to the latestcallbackfunction, ensuring that the most recent version is called without needing to re-register the event listener each timecallbackupdates.useEffect:- Updates
callbackRef.currentwhenevercallbackchanges. - Registers the event listener on the specified
element:- For
windowelements, it directly attaches the listener. - For DOM elements passed as
ref, it useselement.currentto add and remove the listener.
- For
- Cleans up the event listener on component unmount or when
eventTypeorelementchanges.
- Updates
Example Usage
import { useRef } from 'react';
import { useEventListener } from './useEventListener';
function ClickTracker() {
const divRef = useRef(null);
useEventListener({
eventType: 'click',
callback: (e) => console.log('Div clicked!', e),
element: divRef,
});
return <div ref={divRef} style={{ width: '200px', height: '200px', background: 'lightblue' }}>Click me!</div>;
}In this example:
- The
useEventListenerhook adds a click event listener to adivelement, which logs a message to the console each time thedivis clicked. - The hook ensures that the event listener is cleaned up if the component unmounts or the
eventTypeorelementchanges.
useStateWithHistory Hook
The useStateWithHistory hook provides a way to manage state with history tracking, allowing you to navigate backward and forward through previous state values. This is useful in scenarios where users might want to undo or redo actions, similar to browser navigation or an editor's history feature.
## Function Signature
```typescript
export default function useStateWithHistory<T>({
defaultValue,
capacity = 10,
}: StateWithHistoryProps<T>): StateWithHistory<T>Parameters
defaultValue:T
The initial value of the state.capacity:number(optional, default =10)
The maximum number of historical states to keep in memory. When the history exceeds this capacity, the oldest entries are removed.
Return Value
The hook returns an object with the following properties and functions:
value:T
The current state value.set:(v: T) => void
Function to set a new state value. If the new value is different from the current state, it is added to the history.history:T[]
An array representing the history of state values.pointer:number
The current position in the history, indicating which version of the state is currently active.back:() => void
Function to move one step back in the history. If already at the oldest state, it has no effect.forward:() => void
Function to move one step forward in the history. If already at the latest state, it has no effect.go:(index: number) => void
Function to go to a specific position in the history by index. If the index is out of range, it has no effect.
How It Works
- State Management: The
useStatehook manages the primary state (value). - History Tracking:
historyRefholds the array of historical state values.pointerRefkeeps track of the current position withinhistoryRef.- The
setfunction updates the state and manages the history, clearing any "future" states if a new value is set after moving back in history.
- Capacity Management: When the number of historical states exceeds
capacity, the oldest state is removed.
Example Usage
import useStateWithHistory from './useStateWithHistory';
function Counter() {
const {
value,
set,
history,
pointer,
back,
forward,
go,
} = useStateWithHistory({ defaultValue: 0, capacity: 5 });
return (
<div>
<p>Current Value: {value}</p>
<button onClick={() => set(value + 1)}>Increment</button>
<button onClick={() => set(value - 1)}>Decrement</button>
<button onClick={back} disabled={pointer <= 0}>Undo</button>
<button onClick={forward} disabled={pointer >= history.length - 1}>Redo</button>
<p>History: {history.join(', ')}</p>
</div>
);
}In this example:
IncrementandDecrementbuttons modify the current state.UndoandRedobuttons navigate backward and forward through state history, allowing for easy state management and history tracking.- The
historyarray shows the current state history, withpointerindicating the current position in that history.
isOnScreen Hook
The isOnScreen hook determines if a specified element is visible on the screen using the Intersection Observer API. This is useful for detecting when an element enters or leaves the viewport, enabling features like lazy loading, animations, or analytics tracking.
## Function Signature
```typescript
export function isOnScreen(ref: RefObject<HTMLElement>, rootMargin = '0px'): booleanParameters
ref:RefObject<HTMLElement>
A React ref object pointing to the DOM element to observe. This ref should be attached to an element in the component.rootMargin:string(optional, default ='0px')
The margin around the root (viewport) that is used to determine visibility. This parameter accepts values similar to CSS margin (e.g.,'10px 20px'). Increasing the root margin can trigger visibility checks earlier (when the element is still outside the viewport).
Return Value
isVisible:boolean
Returnstrueif the element is currently visible on the screen (within the viewport) andfalseotherwise.
How It Works
- The hook uses an
IntersectionObserverto monitor the visibility of the specifiedrefelement. - When the element's visibility changes (enters or exits the viewport based on
rootMargin), the observer's callback updates theisVisiblestate. - Cleanup: The observer disconnects automatically when the component unmounts or if
refchanges, preventing memory leaks.
Example Usage
import { useRef } from 'react';
import { isOnScreen } from './isOnScreen';
function ImageWithLazyLoad() {
const imageRef = useRef(null);
const onScreen = isOnScreen(imageRef, '100px'); // Trigger 100px before entering viewport
return (
<div ref={imageRef} style={{ height: '300px', background: 'lightgray' }}>
{onScreen ? <img src="image.jpg" alt="Lazy loaded image" /> : <p>Scroll to load image</p>}
</div>
);
}In this example:
- The
isOnScreenhook detects ifimageRefis within 100px of the viewport, allowing the image to load just before it enters the screen. - If the element is not in view, a placeholder text is displayed instead.
useAnimatedNumber Hook
The useAnimatedNumber hook animates a numeric value from a starting value to a target value over a specified duration. It supports options for decimal values, locale-based formatting, and re-triggering the animation when the element comes into view.
## Function Signature
```typescript
export function useAnimatedNumber(
targetValue: number,
ref: React.RefObject<HTMLElement>,
options: UseAnimatedNumberOptions = {}
): stringParameters
targetValue:number
The target value to animate to.ref:React.RefObject<HTMLElement>
A React ref object pointing to the DOM element that should be observed. The animation can be triggered when this element enters the viewport.options:UseAnimatedNumberOptions(optional)
An object to configure animation options.startValue:number(default =0)
The initial value of the animation.duration:number(default =1000)
The duration of the animation in milliseconds.isDecimal:boolean(default =false)
Specifies whether the animated value should be displayed as a decimal.locale:string(default ='en')
The locale used for formatting the animated number, allowing for proper number formatting based on the user’s locale.retriggerOnView:boolean(default =false)
If set totrue, the animation re-triggers each time the element enters the viewport.
Return Value
parsedToLocaleNumber:string
A string representing the animated number formatted to the specified locale.
How It Works
- View Triggering: The hook uses the
isOnScreenhelper to check if the element referenced byrefis in view, triggering the animation when it appears in the viewport. - Animation Calculation:
- Uses
requestAnimationFrameto update the value over time. - Calculates progress based on the
durationand animates fromstartValuetotargetValue. - If
isDecimalistrue, the value is formatted as a decimal.
- Uses
- Locale Formatting: The
Intl.NumberFormatAPI is used to format the animated value according to the specifiedlocale. - Re-triggering: The
retriggerOnViewoption determines if the animation re-triggers when the element re-enters the viewport.
Example Usage
import { useRef } from 'react';
import { useAnimatedNumber } from './useAnimatedNumber';
function AnimatedCounter() {
const counterRef = useRef(null);
const animatedNumber = useAnimatedNumber(100, counterRef, {
startValue: 0,
duration: 2000,
isDecimal: true,
locale: 'en',
retriggerOnView: true,
});
return (
<div ref={counterRef} style={{ fontSize: '2rem', fontWeight: 'bold' }}>
{animatedNumber}
</div>
);
}In this example:
- The number animates from
0to100over2000milliseconds, displaying as a decimal with a locale-specific format. - The animation re-triggers each time the element comes back into view.
useDialog Hook
The useDialog hook provides a convenient way to manage the open and close states of a dialog component. This hook centralizes the dialog state management, providing functions to easily open, close, and toggle the dialog state.
## Function Signature
```typescript
export function useDialog({ open: openProps = false }: UseDialogProps = {}): {
open: boolean;
setDialogOpen: () => void;
setDialogClose: () => void;
setOpen: (open: boolean) => void;
}Parameters
props:UseDialogProps(optional)
An object to configure the initial dialog state.open:boolean(default =false)
The initial state of the dialog (open or closed).
Return Value
The hook returns an object containing the current dialog state and functions to control it:
open:boolean
The current open state of the dialog.truemeans the dialog is open;falsemeans it is closed.setDialogOpen:() => void
A function to set theopenstate totrue, opening the dialog.setDialogClose:() => void
A function to set theopenstate tofalse, closing the dialog.setOpen:(open: boolean) => void
A function to set theopenstate directly, allowing for more flexible state control.
How It Works
- State Management:
- The
useStatehook initializes the dialog state (open) based on theopenparameter inprops. setOpenprovides a direct way to modify the state.
- The
- Convenience Functions:
setDialogOpenandsetDialogClosesimplify toggling the dialog, especially useful for event handlers or dialog controls.
Example Usage
import { useDialog } from './useDialog';
function ExampleDialog() {
const { open, setDialogOpen, setDialogClose } = useDialog({ open: false });
return (
<div>
<button onClick={setDialogOpen}>Open Dialog</button>
{open && (
<div className="dialog">
<p>This is the dialog content.</p>
<button onClick={setDialogClose}>Close Dialog</button>
</div>
)}
</div>
);
}In this example:
Open Dialogbutton callssetDialogOpento open the dialog.Close Dialogbutton inside the dialog callssetDialogCloseto close it.- The
openvariable controls the conditional rendering of the dialog component.
