Skip to content

Commit

Permalink
feat: 🎸 simplify and improve useThrottle hook
Browse files Browse the repository at this point in the history
Create a version of the hook that simply throttled a value, hook that
throttles a function will be called useThrottleFn.

BREAKING CHANGE: 🧨 useThrottle is now a completely different hook
  • Loading branch information
streamich committed Mar 27, 2019
1 parent 6b487c0 commit 452e8d9
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 62 deletions.
36 changes: 7 additions & 29 deletions docs/useThrottle.md
@@ -1,49 +1,27 @@
# `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.
React hook that throttles a value.

## 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]
);
const Demo = ({value}) => {
const throttledValue = useThrottle(value);

return (
<div>
<input
type="text"
value={value}
placeholder="Throttled input"
onChange={({ currentTarget }) => {
setStatus('Updating stopped');
setValue(currentTarget.value);
}}
/>
<div>{status}</div>
<>
<div>Value: {value}</div>
<div>Throttled value: {throttledValue}</div>
</div>
</>
);
};
```

## Reference

```ts
useThrottle(fn, ms: number, args: any[]);
useThrottle(value, ms?: number);
```
49 changes: 49 additions & 0 deletions docs/useThrottleFn.md
@@ -0,0 +1,49 @@
# `useThrottleFn`

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 { useThrottleFn } from 'react-use';

const Demo = () => {
const [status, setStatus] = React.useState('Updating stopped');
const [value, setValue] = React.useState('');
const [throttledValue, setThrottledValue] = React.useState('');

useThrottleFn(
() => {
setStatus('Waiting for input...');
setThrottledValue(value);
},
2000,
[value]
);

return (
<div>
<input
type="text"
value={value}
placeholder="Throttled input"
onChange={({ currentTarget }) => {
setStatus('Updating stopped');
setValue(currentTarget.value);
}}
/>
<div>{status}</div>
<div>Throttled value: {throttledValue}</div>
</div>
);
};
```

## Reference

```ts
useThrottleFn(fn, ms: number, args: any[]);
```
29 changes: 15 additions & 14 deletions src/__stories__/useThrottle.story.tsx
@@ -1,35 +1,36 @@
import * as React from 'react';
import { storiesOf } from '@storybook/react';
import { useThrottle } from '..';
import { useThrottle, useCounter } 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('');
const throttledValue = useThrottle(value, 2000);
const [lastThrottledValue, setLastThrottledValue] = React.useState(throttledValue);
const [count, {inc}] = useCounter();

useThrottle(
() => {
setStatus('Waiting for input...');
setThrottledValue(value);
},
2000,
[value]
);
React.useEffect(() => {
if (lastThrottledValue !== throttledValue) {
setLastThrottledValue(throttledValue);
inc();
}
});

return (
<div>
<div style={{width: 300, margin: '40px auto'}}>
<input
type="text"
value={value}
placeholder="Throttled input"
style={{width: '100%'}}
onChange={({ currentTarget }) => {
setStatus('Updating stopped');
setValue(currentTarget.value);
}}
/>
<div>{status}</div>
<br />
<br />
<div>Throttled value: {throttledValue}</div>
<div>Times updated: {count}</div>
</div>
);
};
Expand Down
46 changes: 27 additions & 19 deletions src/useThrottle.ts
@@ -1,28 +1,36 @@
import { useRef, useEffect } from 'react';
import {useState, useRef, useEffect} from 'react';
import useUnmount from './useUnmount'

const useThrottle = (fn: () => any, ms: number = 0, args?) => {
const lastRan = useRef(0);
const useThrottle = <T>(value: T, ms: number = 200) => {
const [state, setState] = useState<T>(value);
let timeout = useRef<any>(null);
const nextValue = useRef(null) as any;
const hasNextValue = useRef(0) as any;

useEffect(() => {
let timeout
const diff = Date.now() - lastRan.current

if (diff >= ms) {
fn.apply(null, args);
lastRan.current = Date.now();
if (!timeout.current) {
setState(value);
const timeoutCallback = () => {
if (hasNextValue.current) {
hasNextValue.current = false;
setState(nextValue.current);
timeout.current = setTimeout(timeoutCallback, ms);
} else {
timeout.current = null;
}
};
timeout.current = setTimeout(timeoutCallback, ms);
} else {
timeout = setTimeout(() => {
fn.apply(null, args);
lastRan.current = Date.now();
}, ms - diff)
nextValue.current = value;
hasNextValue.current = true;
}
}, [value]);

return () => {
if (timeout) {
clearTimeout(timeout);
}
}
}, args);
useUnmount(() => {
clearTimeout(timeout.current);
});

return state;
};

export default useThrottle;

0 comments on commit 452e8d9

Please sign in to comment.