From e6f179d682c09f67dd868a55d281f42ce1215d7f Mon Sep 17 00:00:00 2001 From: Ward Oosterlijnck Date: Tue, 26 Mar 2019 20:00:37 +1100 Subject: [PATCH 1/4] useThrottle --- docs/useThrottle.md | 49 +++++++++++++++++++++++++++ src/__stories__/useThrottle.story.tsx | 39 +++++++++++++++++++++ src/index.ts | 2 ++ src/useThrottle.ts | 28 +++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 docs/useThrottle.md create mode 100644 src/__stories__/useThrottle.story.tsx create mode 100644 src/useThrottle.ts diff --git a/docs/useThrottle.md b/docs/useThrottle.md new file mode 100644 index 0000000000..d3efeed7ad --- /dev/null +++ b/docs/useThrottle.md @@ -0,0 +1,49 @@ +# `useThrottle` + +React hook that invokes a function and then delays subsequent function calls until after wait milliseconds have elapsed since the last time the throttled function was invoked. + +The third argument is the array of values that the throttle depends on, in the same manner as useEffect. The throttle timeout will start when one of the values changes. + +## Usage + +```jsx +import React, { useState } from 'react'; +import { useThrottle } from 'react-use'; + +const Demo = () => { + const [status, setStatus] = React.useState('Updating stopped'); + const [value, setValue] = React.useState(''); + const [throttledValue, setThrottledValue] = React.useState(''); + + useThrottle( + () => { + setStatus('Waiting for input...'); + setThrottledValue(value); + }, + 2000, + [value] + ); + + return ( +
+ { + setStatus('Updating stopped'); + setValue(currentTarget.value); + }} + /> +
{status}
+
Throttled value: {throttledValue}
+
+ ); +}; +``` + +## Reference + +```ts +useThrottle(fn, ms: number, args: any[]); +``` diff --git a/src/__stories__/useThrottle.story.tsx b/src/__stories__/useThrottle.story.tsx new file mode 100644 index 0000000000..85f1d4682e --- /dev/null +++ b/src/__stories__/useThrottle.story.tsx @@ -0,0 +1,39 @@ +import * as React from 'react'; +import { storiesOf } from '@storybook/react'; +import { useThrottle } from '..'; +import ShowDocs from '../util/ShowDocs'; + +const Demo = () => { + const [status, setStatus] = React.useState('Updating stopped'); + const [value, setValue] = React.useState(''); + const [throttledValue, setThrottledValue] = React.useState(''); + + useThrottle( + () => { + setStatus('Waiting for input...'); + setThrottledValue(value); + }, + 2000, + [value] + ); + + return ( +
+ { + setStatus('Updating stopped'); + setValue(currentTarget.value); + }} + /> +
{status}
+
Throttled value: {throttledValue}
+
+ ); +}; + +storiesOf('Side effects|useThrottle', module) + .add('Docs', () => ) + .add('Demo', () => ); diff --git a/src/index.ts b/src/index.ts index 0544e45ada..d6e432cd8d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -43,6 +43,7 @@ import useSetState from './useSetState'; import useSize from './useSize'; import useSpeech from './useSpeech'; import useSpring from './useSpring'; +import useThrottle from './useThrottle'; import useTimeout from './useTimeout'; import useTitle from './useTitle'; import useToggle from './useToggle'; @@ -101,6 +102,7 @@ export { useSize, useSpeech, useSpring, + useThrottle, useTimeout, useTitle, useToggle, diff --git a/src/useThrottle.ts b/src/useThrottle.ts new file mode 100644 index 0000000000..06103ba1ed --- /dev/null +++ b/src/useThrottle.ts @@ -0,0 +1,28 @@ +import { useRef, useEffect } from 'react'; + +const useThrottle = (fn: () => any, ms: number = 0, args: Array = []) => { + const lastRan = useRef(0); + + useEffect(() => { + let timeout + const diff = Date.now() - lastRan.current + + if (diff >= ms) { + fn.apply(null, args); + lastRan.current = Date.now(); + } else { + timeout = setTimeout(() => { + fn.apply(null, args); + lastRan.current = Date.now(); + }, ms - diff) + } + + return () => { + if (timeout) { + clearTimeout(timeout); + } + } + }, args); +}; + +export default useThrottle; From 6d3641e04c1c4c0ff16ad758cdd2b0f4d5dec0b7 Mon Sep 17 00:00:00 2001 From: Ward Oosterlijnck Date: Tue, 26 Mar 2019 20:01:03 +1100 Subject: [PATCH 2/4] Added to README + typo fix --- README.md | 3 ++- docs/useDebounce.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d314ad14e4..f13ddac38d 100644 --- a/README.md +++ b/README.md @@ -71,12 +71,13 @@ - [`useAsync`](./docs/useAsync.md) — resolves an `async` function. - [`useAsyncRetry`](./docs/useAsyncRetry.md) — `useAsync` with `retry()` method. - [`useCss`](./docs/useCss.md) — dynamically adjusts CSS. + - [`useDebounce`](./docs/useDebounce.md) — debounces a function. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/side-effects-usedebounce--demo) - [`useFavicon`](./docs/useFavicon.md) — sets favicon of the page. - [`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) - [`useTitle`](./docs/useTitle.md) — sets title of the page. - - [`useDebounce`](./docs/useDebounce.md) — debounces a function.

- [**Lifecycles**](./docs/Lifecycles.md) diff --git a/docs/useDebounce.md b/docs/useDebounce.md index 52218e16b1..d681f31f41 100644 --- a/docs/useDebounce.md +++ b/docs/useDebounce.md @@ -42,5 +42,5 @@ const Demo = () => { ## Reference ```ts -useDebouce(fn, ms: number, args: any[]); +useDebounce(fn, ms: number, args: any[]); ``` From a4f0197161a02c1aba7d2d21e1c658a3ad1a50d1 Mon Sep 17 00:00:00 2001 From: Ward Oosterlijnck Date: Tue, 26 Mar 2019 20:08:35 +1100 Subject: [PATCH 3/4] Add debounced value to demo and docs --- docs/useDebounce.md | 3 +++ src/__stories__/useDebounce.story.tsx | 3 +++ 2 files changed, 6 insertions(+) diff --git a/docs/useDebounce.md b/docs/useDebounce.md index d681f31f41..20a25b27e6 100644 --- a/docs/useDebounce.md +++ b/docs/useDebounce.md @@ -13,10 +13,12 @@ import { useDebounce } from 'react-use'; const Demo = () => { const [state, setState] = React.useState('Typing stopped'); const [val, setVal] = React.useState(''); + const [debouncedValue, setDebouncedValue] = React.useState(''); useDebounce( () => { setState('Typing stopped'); + setDebouncedValue(val); }, 2000, [val] @@ -34,6 +36,7 @@ const Demo = () => { }} />
{state}
+
Debounced value: {debouncedValue}
); }; diff --git a/src/__stories__/useDebounce.story.tsx b/src/__stories__/useDebounce.story.tsx index febf69692e..799fa7ee82 100644 --- a/src/__stories__/useDebounce.story.tsx +++ b/src/__stories__/useDebounce.story.tsx @@ -6,10 +6,12 @@ import ShowDocs from '../util/ShowDocs'; const Demo = () => { const [state, setState] = React.useState('Typing stopped'); const [val, setVal] = React.useState(''); + const [debouncedValue, setDebouncedValue] = React.useState(''); useDebounce( () => { setState('Typing stopped'); + setDebouncedValue(val); }, 2000, [val] @@ -27,6 +29,7 @@ const Demo = () => { }} />
{state}
+
Debounced value: {debouncedValue}
); }; From c51eb7fe6a2d885f349ccc9ed9acfca48783dfed Mon Sep 17 00:00:00 2001 From: Ward Oosterlijnck Date: Tue, 26 Mar 2019 21:43:27 +1100 Subject: [PATCH 4/4] args optional --- src/useThrottle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/useThrottle.ts b/src/useThrottle.ts index 06103ba1ed..c82a044fe4 100644 --- a/src/useThrottle.ts +++ b/src/useThrottle.ts @@ -1,6 +1,6 @@ import { useRef, useEffect } from 'react'; -const useThrottle = (fn: () => any, ms: number = 0, args: Array = []) => { +const useThrottle = (fn: () => any, ms: number = 0, args?) => { const lastRan = useRef(0); useEffect(() => {