This package contains common hooks and components to use in a React application.
npm i @cdoublev/react-utils
@cdoublev/react-utils is built to run in the current version of NodeJS, which means it should be transpiled with your application using its own targets.
useAnimate abstracts using Element.animate() from the Web Animation API (MDN).
useAnimate :: Ref -> Animate
Ref should be a React reference object containing an Element, ie. Ref => { current: Element }.
Animate is a Function that has the following signature: Animate :: (Keyframes -> Options?|Number?) -> Animation.
Animation (W3C) will be cancelled if it's still running when the component unmounts.
Example: CodePen
useAnimateCustom abstracts using animate(), a lightweight alternative to Element.animate() or its official polyfill, with some extra features.
Note: this hook relies on @cdoublev/animate as an optional dependency to install manually.
useAnimateCustom :: Ref -> Animate
Ref should be a React reference object containing an Element, ie. Ref => { current: Element }.
Animate is a Function that has the following signature: Animate :: (Keyframes|MotionPath -> Options?|Number?) -> Animation.
Animation conforms to the native Animation (W3C). It will be cancelled if it's still running when the component unmouts.
Example: CodePen
Related:
useIntersectionObserver abstracts using an IntersectionObserver to execute a function when an Element intersects an ancestor, ie. when it enters in or exits from its viewport.
useIntersectionObserver :: Configuration -> [CallbackRef, Identifier -> CallbackRef, Ref]
The first CallbackRef should be used to define root, ie. an ancestor containing the Elements to observe, defined with the second callback ref obtained by executing the higher order function with an Identifier (String|Number|Symbol) for each Element to observe.
Both should be used. root will default to null (ie. document) when the corresponding callback ref is executed without an argument. null can't be used to set root to document because React will execute the callback ref with null before unmount, if it's used as a ref prop.
Ref is a React object ref containing the current IntersectionObserver. It can be used eg. to manually unobserve a target after a first intersection.
Each observed Element will be unobserved before unmount, and the current IntersectionObserver will be disconnected before root unmounts, except if root corresponds to document, as it could be shared with other components. Only one IntersectionObserver will be created for each unique set of intersection options.
Example: CodePen
Configuration:
rootMarginandthresholdare two of the threeIntersectionObserveroptions (W3C), the third beingrootonEnterandonExitare optional callbacks executed with the arguments received from theIntersectionObservercallback when anElemententers in or exits from the viewport of its ancestor
Note: make sure to use a memoized value for threshold if it's an Array, as well as for onEnter and onExit.
Credit: Dan Abramov.
useInterval abstracts using setInterval() and clearInterval() to schedule and repeat the execution of a function over time, without worrying about cancelling the timer to avoid a memory leak such as a React state update on an unmounted component.
useInterval :: [Function, Number] -> void
It will stop executing Function if:
- the component unmounts
- the reference to
Functionhas changed - the delay (
Number) has changed
useGatherMemo abstracts gathering (merging) and/or picking (destructuring) properties from object(s) while memoizing the result to avoid unneeded updates in the component and its children.
It's a low level hook that can be usefull eg. when you want to merge options or props received in a hook or a component with a large default options object, instead of listing each option argument with a default value and/or listing each one as a dependency of a hook.
useGatherMemo :: (Object -> ...String|Symbol) -> [x, Object]
Example:
const options = { color: 'red', size: 'large' }
/**
* 1. Pick prop(s) and gather the rest
*
* Both constants will be defined with a memoized value/reference, if `options`
* shallow equals its previous render value
*/
// Write this:
const [color, subOptions] = useGatherMemo(options, 'color')
// Instead of this:
const { color, ...subOptions } = options
/**
* 2. Gather properties
*
* `allOptions` will be defined with a memoized reference, if `subOptions`
* shallow equals its previous render value.
*/
// Write this:
const allOptions = useGatherMemo({ ...subOptions, display: 'fullscreen' })
// Instead of this:
const allOptions = { ...subOptions, display: 'fullscreen' }Warning: don't over use it, ie. use it only with large objects, otherwise it will negatively impact performances by increasing the call stack as well as the amount of data stored in memory.
useLazyStateUpdate abstracts delaying a state update.
Give it a state value and a delay (default to 100 ms) and it will update the component and return the corresponding state when delay is over.
It could be used eg. to delay the render of an error notice after validating an input value updated on each user input.
useLazyStateUpdate :: [a, Number] -> a
useMediaQuery abstracts using windows.matchMedia() to observe a match against a query, eg. (min-width: 50em).
useMediaQuery :: String -> Boolean
useScrollIntoView abstracts using Element.scrollIntoView() when a scroll event is emitted.
Depending on the scroll direction, it prevents the default scroll behavior and scrolls into view the next or previous Element, on:
- ✅ touch (finger or stylus) move
- ✅ wheel (rolling)
- ❌ wheel (button)
useScrollIntoView :: Configuration -> [CallbackRef, Identifier -> CallbackRef, Ref]
The first CallbackRef should be used to define root, ie. an ancestor containing the Elements to scroll into view, defined with the second callback ref, obtained by executing the higher order function with a unique Identifier (String|Number|Symbol) for each Element.
Both should be used. To set document as root, the corresponding callback ref should be executed without an argument. See useIntersectionObserver to know why, since this hook is used to set the previous/next Element to scroll into view when an Element enters in the viewport of root.
Ref is a React object ref containing the current IntersectionObserver.
Example:
Configuration:
beforeScrollis an optional callback executed before scrolling, that can be used to set theElementto scroll into view (by returning its index value intargets) or eg. to set a CSS transition classname before scrolling, and receiving as arguments (1) the index of the target that will be scrolled into view, (2) the current target index and (3) the scrolling direction (up,down,left, orright)delay(default to200ms) is a timeout value before scrollingdirections(default toboth) is the scroll direction to listen on (x,y, orboth)wait(default to1000ms) is a timeout value between two authorized scroll eventsmode(default toauto) is the scrolling behavioronEnterandonExitare optional callbacks defined inuseIntersectionObservertouchSensitivity(default to150) is a distance in pixels that the finger or stylus should move to be handled as a scroll event
useSVGMousePosition abstracts translating the position of the mouse relative to an <svg> in document, into a position relative to its viewBox.
It could be used eg. to animate the position of a child SVGElement (paths, filters, clips, masks, gradients, etc...).
useSVGMousePosition :: Configuration -> [Position, CallbackRef, CallbackRef]
Position are the coordinates of the mouse: Position => { x: Float, y: Float }.
The first CallbackRef should be used to define the <svg>. Using both callbacks is usefull to listen mousemove events in an ancestor Element containing the <svg> and animate the position of a child SVGElement outside of the <svg>.
Note: the <svg> should preserve its aspect ratio, otherwise Position will be incorrect, as the current implementation is using Element.getBoundingClientRect() to compute its dimensions.
Example: CodePen
Configuration:
hasRoot(default tofalse) is an optional boolean to make sure that themouseoverevent listener will be registered only when the<svg>is mounted, eg. if it's conditionally rendered in itsrootcomponentinitial(default to{ x: 0, y: 0 }) is an optional initial positionisFixed(default tofalse) is an optionalBooleanto flag thetargetas having a fixed position in the viewport ofdocument, ie. inwindowprecision(default to2) is an optional number to roundPositionvalues
useTimeout abstracts using setTimeout() and clearTimeout() to schedule the execution of a Function, without worrying about cancelling the timer to avoid a memory leak such as a React state update on an unmounted component.
useTimeout :: [Function, Number] -> void
It will stop executing Function if:
- the component unmounts
- the reference to
Functionhas changed - the delay (
Number) has changed
useTransition abstracts scheduling multiple state updates over time using different delays and durations.
It will always return the current state as a collection, which can be conceptualized as a box whose values are entering and exiting in and out over time. It can be used eg. to transition between CSS classnames when a component did mount or before unmouting.
useTransition :: { transitions: [Transition], onExit?: Transition } -> [[x], Restart, Exit?, Boolean?, Enter?]
A Transition ([x, Number, Number?]) is a collection of a state value (x) and one or two Numbers: the first value is the delay before applying the given state, and the second value is the duration during which it should be applied, except for the Transition defined on onExit, defined only with a duration.
Note: transitions should be memoized, otherwhise the inital state will always be applied.
Exit, Enter, and the Boolean are returned only when onExit is provided. Exit is a Function to execute the Transition defined on onExit before toggling the Boolean value to false, indicating that the component can be considered as unmounted. Enter is a Function to toggle this value back to true.
Demo: CodePen.
Related packages:
useValidation abstracts using the Constraint Validation API (MDN) to validate a form field on blur (default) or on change.
useValidation :: { onChange?: Function, onBlur?: Function, validateOnChange?: Boolean } -> [String, Props]
It returns any error message from the Constraint Validation API, and a collection of component properties such as onChange and onBlur event handlers, to assign to an <input>, <select> or <textarea>. Each of those handlers will be composed with a corresponding handler given as argument.
<Filter> provides common filter effects to use in a SVGElement.
Usage for a single filter effect:
<svg viewBox='0 0 100 100'>
<Filter id='glow-large' name='glow' blur='10' spread='3' opacity='0.3' />
<Filter id='glow-small' name='glow' blur='5' spread='2' opacity='0.7' />
<circle filter='url(#glow-large)' cx='25' cy='25' r='25'>
<circle filter='url(#glow-small)' cx='75' cy='75' r='25'>
</svg>When used alone, <Filter> should not have a in or result and it will automatically be wrapped in a <filter> with the following default prop values:
'colorInterpolation'(for thecolor-interpolation-filterattribute):'sRGB''id':'name''width'and'height': based on'name''x'and'y': based on'width'and'height'
'width', 'height', 'x', 'y' should be provided as percentage values.
All effect's names are listed further below.
Usage for composing filter effects:
<svg viewBox='0 0 100 100'>
<filter id='glow-noise' x='-100%' y='-100%' height='300%' width='300%'>
<Filter name='glow' blur='10' spread='3' />
<Filter name='noise' in='glow' opacity='0.2' frequency='0.2' />
</filter>
<circle filter='url(#glow-noise)' cx='50' cy='50' r='25'>
</svg>When composed, <Filter>s should have a in and eventually a result prop (defaults to name and not required for the last <Filter> in the composition).
Effect names and props:
| Name | Props |
|---|---|
| color-correction | lightness, opacity, saturation |
| glow | blur, spread, lightness, opacity |
| glow-inset | blur, threshold, lightness, opacity |
| gooey | tolerance |
| noise | frequency, blend, color, lightness, opacity |
| shadow | color, offsetX, offsetY, blur, spread, threshold, opacity, saturation |
| shadow-inset | color, offsetX, offsetY, blur, spread, threshold, opacity, saturation |
Default values:
- color:
'black' - lightness:
1 - opacity:
0.5 - offsetX:
0 - offsetY:
0 - saturation:
1 - spread:
0 - threshold:
0
All props require a number, except blend (CSS blend mode) and color (CSS color).