feat: 🎸 add useGetSetState

This commit is contained in:
streamich 2018-10-29 17:41:34 +01:00
parent efa9acd988
commit dcd1013736
6 changed files with 83 additions and 6 deletions

View File

@ -75,6 +75,7 @@
<br/>
- [**State**](./docs/State.md)
- [`useGetSet`](./docs/useGetSet.md) &mdash; returns state getter `get()` instead of raw state.
- [`useGetSetState`](./docs/useGetSetState.md) &mdash; as if [`useGetSet`](./docs/useGetSet.md) and [`useSetState`](./docs/useSetState.md) had a baby.
- [`useObservable`](./docs/useObservable.md) &mdash; tracks latest value of an `Observable`.
- [`useSetState`](./docs/useSetState.md) &mdash; creates `setState` method which works like `this.setState`. [![][img-demo]](https://codesandbox.io/s/n75zqn1xp0)
- [`useToggle` and `useBoolean`](./docs/useToggle.md) &mdash; tracks state of a boolean.

23
docs/useGetSetState.md Normal file
View File

@ -0,0 +1,23 @@
# `useGetSetState`
A mix of `useGetSet` and `useGetSetState`.
## Usage
```jsx
import {useGetSetState} from 'react-use';
const Demo = () => {
const [get, setState] = useGetSetState({cnt: 0});
const onClick = () => {
setTimeout(() => {
setState({cnt: get().cnt + 1})
}, 1_000);
};
return (
<button onClick={onClick}>Clicked: {get().cnt}</button>
);
};
```

View File

@ -0,0 +1,23 @@
import * as React from 'react';
import {storiesOf} from '@storybook/react';
import {useGetSetState, useSetState} from '..';
import ShowDocs from '../util/ShowDocs';
const Demo = () => {
const [get, setState] = useGetSetState<{cnt: number}>({cnt: 0});
const onClick = () => {
setTimeout(() => {
setState({cnt: get().cnt + 1})
}, 1_000);
};
return (
<button onClick={onClick}>Clicked: {get().cnt}</button>
);
};
storiesOf('useGetSetState', module)
.add('Docs', () => <ShowDocs md={require('../../docs/useGetSetState.md')} />)
.add('Demo', () =>
<Demo/>
)

View File

@ -7,6 +7,7 @@ import useCss from './useCss';
import useFavicon from './useFavicon';
import useGeolocation from './useGeolocation';
import useGetSet from './useGetSet';
import useGetSetState from './useGetSetState';
import useHover from './useHover';
import useIdle from './useIdle';
import useLifecycles from './useLifecycles';
@ -45,6 +46,7 @@ export {
useFavicon,
useGeolocation,
useGetSet,
useGetSetState,
useHover,
useIdle,
useLifecycles,

View File

@ -48,7 +48,7 @@ const useAudio = (props: AudioProps): [React.ReactElement<AudioProps>, AudioStat
const onPlay = () => setState({isPlaying: true});
const onPause = () => setState({isPlaying: false});
const onVolumeChange = (event) => {
const onVolumeChange = () => {
const el = ref.current;
if (!el) return;
setState({
@ -56,7 +56,7 @@ const useAudio = (props: AudioProps): [React.ReactElement<AudioProps>, AudioStat
volume: el.volume,
});
};
const onDurationChange = (event) => {
const onDurationChange = () => {
const el = ref.current;
if (!el) return;
const {duration, buffered} = el;
@ -70,7 +70,7 @@ const useAudio = (props: AudioProps): [React.ReactElement<AudioProps>, AudioStat
if (!el) return;
setState({time: el.currentTime});
};
const onProgress = (event) => {
const onProgress = () => {
const el = ref.current;
if (!el) return;
setState({buffered: parseTimeRanges(el.buffered)});
@ -102,7 +102,7 @@ const useAudio = (props: AudioProps): [React.ReactElement<AudioProps>, AudioStat
if (!lockPlay) {
const promise = el.play();
const isPromise = typeof promise === 'object';
if (isPromise) {
lockPlay = true;
const resetLock = () => {
@ -152,14 +152,14 @@ const useAudio = (props: AudioProps): [React.ReactElement<AudioProps>, AudioStat
if (!el) {
if (process.env.NODE_ENV !== 'production') {
console.error(
'useAudio() ref to <audio> element is empty at mount. ' +
'useAudio() ref to <audio> element is empty at mount. ' +
'It seem you have not rendered the audio element, which is ' +
'returns as the first argument const [audio] = useAudio(...).'
);
}
return;
}
// Start media, if autoPlay requested.
if (props.autoPlay && el.paused) {
controls.play();

28
src/useGetSetState.ts Normal file
View File

@ -0,0 +1,28 @@
import {useRef} from './react';
import useUpdate from './useUpdate';
const useGetSetState = <T extends object>(initialState: T = {} as T): [() => T, (patch: Partial<T>) => void]=> {
if (process.env.NODE_ENV !== 'production') {
if (typeof initialState !== 'object') {
throw new TypeError('useGetSetState initial state must be an object.');
}
}
const update = useUpdate();
const state = useRef<T>({...(initialState as object)} as T);
const get = () => state.current;
const set = (patch: Partial<T>) => {
if (!patch) return;
if (process.env.NODE_ENV !== 'production') {
if (typeof patch !== 'object') {
throw new TypeError('useGetSetState setter patch must be an object.');
}
}
Object.assign(state.current, patch);
update();
};
return [get, set];
};
export default useGetSetState;