Merge pull request #1203 from ClementParis016/pr/use-latest

feat: add useLatest hook
This commit is contained in:
Vadim Dalecky 2020-05-18 16:14:45 +02:00 committed by GitHub
commit 56c2c93bf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 90 additions and 1 deletions

View File

@ -131,6 +131,7 @@
- [`useDefault`](./docs/useDefault.md) — returns the default value when state is `null` or `undefined`.
- [`useGetSet`](./docs/useGetSet.md) — returns state getter `get()` instead of raw state.
- [`useGetSetState`](./docs/useGetSetState.md) — as if [`useGetSet`](./docs/useGetSet.md) and [`useSetState`](./docs/useSetState.md) had a baby.
- [`useLatest`](./docs/useLatest.md) — returns the latest state or props
- [`usePrevious`](./docs/usePrevious.md) — returns the previous state or props. [![][img-demo]](https://codesandbox.io/s/fervent-galileo-krgx6)
- [`usePreviousDistinct`](./docs/usePreviousDistinct.md) — like `usePrevious` but with a predicate to determine if `previous` should update.
- [`useObservable`](./docs/useObservable.md) — tracks latest value of an `Observable`.

36
docs/useLatest.md Normal file
View File

@ -0,0 +1,36 @@
# `useLatest`
React state hook that returns the latest state as described in the [React hooks FAQ](https://reactjs.org/docs/hooks-faq.html#why-am-i-seeing-stale-props-or-state-inside-my-function).
This is mostly useful to get access to the latest value of some props or state inside an asynchronous callback, instead of that value at the time the callback was created from.
## Usage
```jsx
import { useLatest } from 'react-use';
const Demo = () => {
const [count, setCount] = React.useState(0);
const latestCount = useLatest(count);
function handleAlertClick() {
setTimeout(() => {
alert(`Latest count value: ${latestCount.current}`);
}, 3000);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
<button onClick={handleAlertClick}>Show alert</button>
</div>
);
};
```
## Reference
```ts
const latestState = useLatest = <T>(state: T): MutableRefObject<T>;
```

View File

@ -42,6 +42,7 @@ export { default as createBreakpoint } from './createBreakpoint';
// export { default as useKeyboardJs } from './useKeyboardJs';
export { default as useKeyPress } from './useKeyPress';
export { default as useKeyPressEvent } from './useKeyPressEvent';
export { default as useLatest } from './useLatest';
export { default as useLifecycles } from './useLifecycles';
export { default as useList } from './useList';
export { default as useLocalStorage } from './useLocalStorage';
@ -111,4 +112,4 @@ export { useRendersCount } from './useRendersCount';
export { useFirstMountState } from './useFirstMountState';
export { default as useSet } from './useSet';
export { createGlobalState } from './createGlobalState';
export { useHash } from './useHash'
export { useHash } from './useHash';

View File

@ -0,0 +1,28 @@
import { storiesOf } from '@storybook/react';
import * as React from 'react';
import { useLatest } from '../src';
import ShowDocs from './util/ShowDocs';
const Demo = () => {
const [count, setCount] = React.useState(0);
const latestCount = useLatest(count);
const timeoutMs = 3000;
function handleAlertClick() {
setTimeout(() => {
alert(`Latest count value: ${latestCount.current}`);
}, timeoutMs);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
<button onClick={handleAlertClick}>Show alert in {timeoutMs / 1000}s</button>
</div>
);
};
storiesOf('State|useLatest', module)
.add('Docs', () => <ShowDocs md={require('../docs/useLatest.md')} />)
.add('Demo', () => <Demo />);

23
tests/useLatest.test.ts Normal file
View File

@ -0,0 +1,23 @@
import { renderHook } from '@testing-library/react-hooks';
import useLatest from '../src/useLatest';
const setUp = () => renderHook(({ state }) => useLatest(state), { initialProps: { state: 0 } });
it('should return a ref with the latest value on initial render', () => {
const { result } = setUp();
expect(result.current).toEqual({ current: 0 });
});
it('should always return a ref with the latest value after each update', () => {
const { result, rerender } = setUp();
rerender({ state: 2 });
expect(result.current).toEqual({ current: 2 });
rerender({ state: 4 });
expect(result.current).toEqual({ current: 4 });
rerender({ state: 6 });
expect(result.current).toEqual({ current: 6 });
});