diff --git a/README.md b/README.md index 9389ff6464..40b6a3611e 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ - [`useLocalStorage`](./docs/useLocalStorage.md) — manages a value in `localStorage`. - [`useLockBodyScroll`](./docs/useLockBodyScroll.md) — lock scrolling of the body element. - [`useSessionStorage`](./docs/useSessionStorage.md) — manages a value in `sessionStorage`. - - [`useThrottle`](./docs/useThrottle.md) — throttles a function. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/side-effects-usethrottle--demo) + - [`useThrottle` and `useThrottleFn`](./docs/useThrottle.md) — throttles a function. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/side-effects-usethrottle--demo) - [`useTitle`](./docs/useTitle.md) — sets title of the page.

diff --git a/docs/useThrottle.md b/docs/useThrottle.md index 60c1cae86f..7557cee256 100644 --- a/docs/useThrottle.md +++ b/docs/useThrottle.md @@ -1,15 +1,16 @@ -# `useThrottle` +# `useThrottle` and `useThrottleFn` -React hook that throttles a value. +React hooks that throttle. ## Usage ```jsx import React, { useState } from 'react'; -import { useThrottle } from 'react-use'; +import { useThrottle, useThrottleFn } from 'react-use'; const Demo = ({value}) => { const throttledValue = useThrottle(value); + // const throttledValue = useThrottleFn(value => value, 200, [value]); return ( <> @@ -24,4 +25,5 @@ const Demo = ({value}) => { ```ts useThrottle(value, ms?: number); +useThrottleFn(fn, ms, args); ``` diff --git a/src/__stories__/useThrottleFn.story.tsx b/src/__stories__/useThrottleFn.story.tsx new file mode 100644 index 0000000000..ab97a63001 --- /dev/null +++ b/src/__stories__/useThrottleFn.story.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import { storiesOf } from '@storybook/react'; +import { useThrottleFn, useCounter } from '..'; +import ShowDocs from '../util/ShowDocs'; + +const Demo = () => { + const [value, setValue] = React.useState(''); + const throttledValue = useThrottleFn(value => value, 2000, [value]); + const [lastThrottledValue, setLastThrottledValue] = React.useState(throttledValue); + const [count, {inc}] = useCounter(); + + React.useEffect(() => { + if (lastThrottledValue !== throttledValue) { + setLastThrottledValue(throttledValue); + inc(); + } + }); + + return ( +
+ { + setValue(currentTarget.value); + }} + /> +
+
+
Throttled value: {throttledValue}
+
Times updated: {count}
+
+ ); +}; + +storiesOf('Side effects|useThrottleFn', module) + .add('Docs', () => ) + .add('Demo', () => ); diff --git a/src/index.ts b/src/index.ts index 55a3844fe8..9046830812 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,6 +46,7 @@ import useSize from './useSize'; import useSpeech from './useSpeech'; import useSpring from './useSpring'; import useThrottle from './useThrottle'; +import useThrottleFn from './useThrottleFn'; import useTimeout from './useTimeout'; import useTitle from './useTitle'; import useToggle from './useToggle'; @@ -107,6 +108,7 @@ export { useSpeech, useSpring, useThrottle, + useThrottleFn, useTimeout, useTitle, useToggle, diff --git a/src/useThrottleFn.ts b/src/useThrottleFn.ts new file mode 100644 index 0000000000..85e44d7205 --- /dev/null +++ b/src/useThrottleFn.ts @@ -0,0 +1,36 @@ +import {useState, useRef, useEffect} from 'react'; +import useUnmount from './useUnmount' + +const useThrottleFn = (fn: (...args: any[]) => T, ms: number = 200, args: any[]) => { + const [state, setState] = useState(null as any); + let timeout = useRef(null); + const nextArgs = useRef(null) as any; + const hasNextArgs = useRef(false) as any; + + useEffect(() => { + if (!timeout.current) { + setState(fn(...args)); + const timeoutCallback = () => { + if (hasNextArgs.current) { + hasNextArgs.current = false; + setState(fn(...nextArgs.current)); + timeout.current = setTimeout(timeoutCallback, ms); + } else { + timeout.current = null; + } + }; + timeout.current = setTimeout(timeoutCallback, ms); + } else { + nextArgs.current = args; + hasNextArgs.current = true; + } + }, args); + + useUnmount(() => { + clearTimeout(timeout.current); + }); + + return state; +}; + +export default useThrottleFn;