https://github.com/haensl/hooks
Assorted React hooks.
https://github.com/haensl/hooks
debounce hooks react react-components react-hooks reactjs requestanimationframe
Last synced: 3 months ago
JSON representation
Assorted React hooks.
- Host: GitHub
- URL: https://github.com/haensl/hooks
- Owner: haensl
- License: mit
- Created: 2020-06-15T19:10:29.000Z (about 5 years ago)
- Default Branch: master
- Last Pushed: 2024-10-18T23:59:57.000Z (9 months ago)
- Last Synced: 2024-10-19T02:59:20.512Z (9 months ago)
- Topics: debounce, hooks, react, react-components, react-hooks, reactjs, requestanimationframe
- Language: JavaScript
- Homepage: https://www.npmjs.com/package/@haensl/hooks
- Size: 1.63 MB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# @haensl/react-hooks
Assorted React hooks.
[](https://nodei.co/npm/@haensl%2Freact-hooks/)
[](http://badge.fury.io/js/@haensl%2Freact-hooks)
[](https://circleci.com/gh/haensl/hooks)### Via `npm`
```bash
$ npm install -S @haensl/react-hooks
```### Via `yarn`
```bash
$ yarn add @haensl/react-hooks
```## Usage
1. [Install @haensl/react-hooks](#installation)
2. Use hooks in your components:
```javascript
import { useDebounce } from '@haensl/react-hooks';const DebouncedButton = () => {
const handler = useDebounce(() => {
console.log('click');
}, 50);return (
click me
);
};
```## Available hooks
* [`useAnimationFrame`](#useAnimationFrame): animate a function.
* [`useBoundingClientRect`](#useBoundingClientRect): keep track of a container's [DOM rectangle](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
* [`useClassNames`](#useClassNames): compile CSS class names from state.
* [`useDebounce`](#useDeboune): debounce a function.
* [`useInterval`](#useInterval): use an interval.
* [`useIsMounted`](#useIsMounted): keep track of whether or not a component is mounted.
* [`useIsomorphicLayoutEffect`](#useIsomorphicLayoutEffect): use this instead of [`useLayoutEffect`](https://reactjs.org/docs/hooks-reference.html#uselayouteffect) if your app uses serverside rendering (SSR).
* [`useIsScrolling`](#useIsScrolling): keep track of whether or not the user is scrolling.
* [`useLang`](#useLang): use the browser's language setting.
* [`useOnScroll`](#useOnScroll): subscribe to scroll events.
* [`usePrevious`](#usePrevious): keep track of a variable's previous value.
* [`useTimeout`](#useTimeout): use a timeout.
* [`useWindowScroll`](#useWindowScroll): keep track of the `window`'s scroll position.
* [`useWindowSize`](#useWindowSize): keep track of the `window`'s size.Uses [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) to animate a function `fn`. The callback is passed one single argument, the time delta in milliseconds that has passed between this and the last call. Please check the [example](#useAnimationFrameExample) below as well as the [Codepen example](https://codepen.io/haensl/pen/GRoNGNB).
```javascript
import React, { useState, useEffect } from 'react';
import { useAnimationFrame } from '@haensl/react-hooks';const AnimatedTimer = () => {
const [seconds, setSeconds] = useState(0);
const [elapsed, setElapsed] = useState(0);useAnimationFrame((dt) => {
setElapsed(elapsed + dt);
});useEffect(() => {
if (elapsed >= 1000) {
setSeconds(seconds + 1);
setElapsed(elapsed - 1000);
}
}, [elapsed]);return (
{ seconds }
);
};
```#### [→ Codepen example](https://codepen.io/haensl/pen/GRoNGNB)
### `useBoundingClientRect(ref, [debounceMs = 25])`
Returns the DOM rectangle _(initially `null`)_ as returned by [`getBoundingClientRect`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) for the given container `ref`. Changes are debounced by 25 milliseconds by default. Customize the debounce interval via the optional `debounceMs` argument. Please check out the [example below](#useBoundingClientRectExample) as well as the [Codepen example](https://codepen.io/haensl/pen/YzwxqOq).#### Example
```javascript
import React, { useRef } from 'react';
import { useBoundingClientRect } from '@haensl/react-hooks';const RectTracker = () => {
const ref = useRef();
const containerRect = useBoundingClientRect(ref);if (!containerRect) {
return (
no container rect
);
}return (
Container rect:
Width: {containerRect.width}
Height: {containerRect.height}
);
};
```#### [→ Codepen example](https://codepen.io/haensl/pen/YzwxqOq)
### `useClassNames(states, [separator = ' '])`
Compiles a `states` object into a CSS class name string. By default all keys in `states` are joined by a space (`' '`) but you can supply a custom `separator` to cater to the needs of your CSS module naming methodology of choice. Please check the [examples below](#useClassNamesExample).
```javascript
import React, { useState } from 'react';
import { useClassNames } from '@haensl/react-hooks';const MyComponent = () => {
const [stateA, setStateA] = useState(false);
const className = useClassNames({
MyComponent: true, // always have MyComponent in class name
MyComponent--stateA: stateA // add MyComponent--stateA when stateA is true
});// className will be 'MyComponent' or 'MyComponent MyComponent--stateA'
return (
{
// render content
}
);
};
```#### Example: custom separator
```javascript
import React, { useState } from 'react';
import { useClassNames } from '@haensl/react-hooks';const MyComponent = () => {
const [stateA, setStateA] = useState(false);
const className = useClassNames(
{
MyComponent: true, // always have MyComponent in class name
stateA // add --stateA when stateA is true
},
'--'
);// className will either be 'MyComponent' or 'MyComponent--stateA'
return (
{
// render content
}
);
};
```### `useDebounce(fn, debounceMs)`
Uses [memoization](https://reactjs.org/docs/hooks-reference.html#usememo) to debounce `fn` by `debounceMs` milliseconds. Please check the [example below](#useDebounceExample) as well as the [Codepen example](https://codepen.io/haensl/pen/eYJBKEZ).
```javascript
import React from 'react';
import { useDebounce } from '@haensl/react-hooks';const DebouncedButton = () => {
const handler = useDebounce(() => {
console.log('click');
}, 50); // handler only fires when there were no calls for 50ms.return (
click me
);
};
```#### [→ Codepen example](https://codepen.io/haensl/pen/eYJBKEZ)
### `useInterval(fn, intervalMs)`
Calls a `fn` repeatedly every `intervalMs` milliseconds.
##### Example
```javascript
import React, { useState, useCallback } from 'react';
import { useInterval } from '@haensl/react-hooks';const MyAnimation = () => {
const [frame, setFrame] = useState(0);// Update frame every 100 milliseconds
useInterval(() => {
setFrame((frame) => frame + 1);
}, 100);return (
{ frame }
);
};
```Returns a `function` to check whether or not the component invoking the hook is mounted.
```javascript
import React, { useEffect } from 'react';
import { useIsMounted } from '@haensl/react-hooks';
import api from 'somewhere';const MyComponent = () => {
const isMounted = useIsMounted();
// load some data from the backend
useEffect(() => {
api.fetchData()
.then((data) => {
if (isMounted()) {
// use data only if component is still mounted
}
});
}, []);
}
```### `useIsomorphicLayoutEffect(fn, deps)`
This hooks resolves the common React warning when using `useLayoutEffect` in a serverside environment:
*Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer’s output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://fb.me/react-uselayouteffect-ssr for common fixes.*
`useIsomorphicLayoutEffect` resolves to [`useLayoutEffect`](https://reactjs.org/docs/hooks-reference.html#uselayouteffect) on the client and [`useEffect`](https://reactjs.org/docs/hooks-reference.html#useeffect) on the server. Use this hook instead of `useLayoutEffect` if your app uses serverside rendering (SSR).
```javascript
import React, { useRef } from 'react';
import { useIsomorphicLayoutEffect } from '@haensl/react-hooks';const MyComponent = () => {
const ref = useRef();// prevents serverside rendering warning
useIsomorphicLayoutEffect(() => {
if (ref.current) {
// do stuff with ref
}
}, [ref]);return (
// ...
)
}
```### `useIsScrolling([el = window, scrollEndMs = 100])`
Returns a `boolean` indicating whether or not the user is scrolling. You can subscribe to a specific element via the first argument, `el` _(default: `window`)_. End of scrolling is determined by no incoming scroll events for `scrollEndMs` milliseconds _(default: `100`)_. Please check the [example blow](#useIsScrollingExample) as well as the [Codepen example](https://codepen.io/haensl/pen/qBbqeWz)
```javascript
import React from 'react';
import { useIsScrolling } from '@haensl/react-hooks';const UserScrollTracker = () => {
const isScrolling = useIsScrolling();return (
The user is currently { isScrolling ? '' : 'not' } scrolling
);
};
```#### [→ Codepen example](https://codepen.io/haensl/pen/qBbqeWz)
### `useLang({ defaultLang = 'en' }) => string`
Returns the user's language setting from [`navigator.language`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language). Use the `defaultLang` of the options parameter to set a default language. _(default: `'en`)_.
```javascript
import React from 'react';
import { useLang } from '@haensl/react-hooks';const MyComponent = () => {
const lang = useLang();return (
The user's preferred language is { lang }.
);
};
```### `useOnScroll(fn, [el = window])`
Subscribes to [`scroll`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scroll_event) events on the given element `el` _(default: `window`)_. The callback function `fn` is passed the [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scroll_event). Please check the [example below](#useOnScrollExample) as well as the [Codepen example](https://codepen.io/haensl/pen/wvMoLJK).
##### Example
```javascript
import React, { useState } from 'react';
import { useOnScroll } from '@haensl/react-hooks';const WindowScrollTracker = () => {
const [windowScroll, setWindowScroll] = useState(0);useOnScroll(() => {
setWindowScroll(window.scrollY);
});return (
Window has scrolled down
{ windowScroll }px
);
};
```#### [→ Codepen example](https://codepen.io/haensl/pen/wvMoLJK)
Keeps track of changes to a value, storing it's _previous_ state.
##### Example
```javascript
import { useEffect, useState } from 'react';
import { usePrevious, useWindowScroll } from '@haensl/react-hooks';const ScrollDirectionTracker = () => {
const scrollPosition = useWindowScroll();
const previousScrollPosition = usePrevious(scrollPosition);
const [scrollDirection, setScrollDirection] = useState('down');useEffect(() => {
if (previousScrollPosition.y < scrollPosition.y) {
setScrollDirection('down');
} else if (previousScrollPosition.y > scrollPosition.y) {
setScrollDirection('up');
}
}, [scrollPosition, previousScrollPosition]);return (
User is scrolling
{ scrollDirection }px
);
};
```### `useTimeout(fn, timeoutMs = 0)`
Calls `fn` once after `intervalMs` milliseconds.
##### Example
```javascript
import React, { useState, useCallback } from 'react';
import { useClassNames, useTimeout } from '@haensl/react-hooks';const MyComponent = () => {
const [animate, setAnimate] = useState(false);// Start animation after 1s
useTimeout(() => {
setAnimate(true);
}, 1000);const className = useClassNames({
animate
});return (
// ...
);
};
```### `useWindowScroll([debounceMs = 25])`
Returns an object _(`null` if there is no `window`)_ with properties `x` and `y` reflecting the the scroll position of the `window` or `document`. Scroll position updates are by default debounced by 25 milliseconds. This debounce interval can be customized via the optional `debounceMs` argument. Please check the [example below](#useWindowScrollExample) as well as the [Codepen example](https://codepen.io/haensl/pen/VweMJGm).```javascript
import React, { useState } from 'react';
import { useWindowScroll } from '@haensl/react-hooks';const windowScrollTracker = () => {
const windowScroll = useWindowScroll();if (!windowScroll) {
return (
no scroll poistion
);
}return (
Scroll x: {windowScroll.x}
Scroll y: {windowScroll.y}
);
};
```#### [→ Codepen example](https://codepen.io/haensl/pen/VweMJGm)
### `useWindowSize([debounceMs = 25])`
Returns an object _(initially `null`)_ with properties `width` and `height` reflecting the `innerWidth` and `innerHeight` of the `window` object. Size updates are by default debounced by 25 milliseconds. This debounce interval can be customized via the optional `debounceMs` argument. Please check the [example below](#useWindowSizeExample) as well as the [Codepen example](https://codepen.io/haensl/pen/mdVMVxY).```javascript
import React, { useState } from 'react';
import { useWindowSize } from '@haensl/react-hooks';const WindowSizeTracker = () => {
const windowSize = useWindowSize();if (!windowSize) {
return (
No window size
);
}return (
Window Size:
width: { windowSize.width }px
height: { windowSize.height }px
);
};
```#### [→ Codepen example](https://codepen.io/haensl/pen/mdVMVxY)
## [Changelog](CHANGELOG.md)
## [License](LICENSE)