Skip to content

Commit dcd1013

Browse files
committed
feat: 🎸 add useGetSetState
1 parent efa9acd commit dcd1013

File tree

6 files changed

+83
-6
lines changed

6 files changed

+83
-6
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
<br/>
7676
- [**State**](./docs/State.md)
7777
- [`useGetSet`](./docs/useGetSet.md) &mdash; returns state getter `get()` instead of raw state.
78+
- [`useGetSetState`](./docs/useGetSetState.md) &mdash; as if [`useGetSet`](./docs/useGetSet.md) and [`useSetState`](./docs/useSetState.md) had a baby.
7879
- [`useObservable`](./docs/useObservable.md) &mdash; tracks latest value of an `Observable`.
7980
- [`useSetState`](./docs/useSetState.md) &mdash; creates `setState` method which works like `this.setState`. [![][img-demo]](https://codesandbox.io/s/n75zqn1xp0)
8081
- [`useToggle` and `useBoolean`](./docs/useToggle.md) &mdash; tracks state of a boolean.

docs/useGetSetState.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# `useGetSetState`
2+
3+
A mix of `useGetSet` and `useGetSetState`.
4+
5+
6+
## Usage
7+
8+
```jsx
9+
import {useGetSetState} from 'react-use';
10+
11+
const Demo = () => {
12+
const [get, setState] = useGetSetState({cnt: 0});
13+
const onClick = () => {
14+
setTimeout(() => {
15+
setState({cnt: get().cnt + 1})
16+
}, 1_000);
17+
};
18+
19+
return (
20+
<button onClick={onClick}>Clicked: {get().cnt}</button>
21+
);
22+
};
23+
```
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as React from 'react';
2+
import {storiesOf} from '@storybook/react';
3+
import {useGetSetState, useSetState} from '..';
4+
import ShowDocs from '../util/ShowDocs';
5+
6+
const Demo = () => {
7+
const [get, setState] = useGetSetState<{cnt: number}>({cnt: 0});
8+
const onClick = () => {
9+
setTimeout(() => {
10+
setState({cnt: get().cnt + 1})
11+
}, 1_000);
12+
};
13+
14+
return (
15+
<button onClick={onClick}>Clicked: {get().cnt}</button>
16+
);
17+
};
18+
19+
storiesOf('useGetSetState', module)
20+
.add('Docs', () => <ShowDocs md={require('../../docs/useGetSetState.md')} />)
21+
.add('Demo', () =>
22+
<Demo/>
23+
)

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import useCss from './useCss';
77
import useFavicon from './useFavicon';
88
import useGeolocation from './useGeolocation';
99
import useGetSet from './useGetSet';
10+
import useGetSetState from './useGetSetState';
1011
import useHover from './useHover';
1112
import useIdle from './useIdle';
1213
import useLifecycles from './useLifecycles';
@@ -45,6 +46,7 @@ export {
4546
useFavicon,
4647
useGeolocation,
4748
useGetSet,
49+
useGetSetState,
4850
useHover,
4951
useIdle,
5052
useLifecycles,

src/useAudio.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ const useAudio = (props: AudioProps): [React.ReactElement<AudioProps>, AudioStat
4848

4949
const onPlay = () => setState({isPlaying: true});
5050
const onPause = () => setState({isPlaying: false});
51-
const onVolumeChange = (event) => {
51+
const onVolumeChange = () => {
5252
const el = ref.current;
5353
if (!el) return;
5454
setState({
5555
muted: el.muted,
5656
volume: el.volume,
5757
});
5858
};
59-
const onDurationChange = (event) => {
59+
const onDurationChange = () => {
6060
const el = ref.current;
6161
if (!el) return;
6262
const {duration, buffered} = el;
@@ -70,7 +70,7 @@ const useAudio = (props: AudioProps): [React.ReactElement<AudioProps>, AudioStat
7070
if (!el) return;
7171
setState({time: el.currentTime});
7272
};
73-
const onProgress = (event) => {
73+
const onProgress = () => {
7474
const el = ref.current;
7575
if (!el) return;
7676
setState({buffered: parseTimeRanges(el.buffered)});
@@ -102,7 +102,7 @@ const useAudio = (props: AudioProps): [React.ReactElement<AudioProps>, AudioStat
102102
if (!lockPlay) {
103103
const promise = el.play();
104104
const isPromise = typeof promise === 'object';
105-
105+
106106
if (isPromise) {
107107
lockPlay = true;
108108
const resetLock = () => {
@@ -152,14 +152,14 @@ const useAudio = (props: AudioProps): [React.ReactElement<AudioProps>, AudioStat
152152
if (!el) {
153153
if (process.env.NODE_ENV !== 'production') {
154154
console.error(
155-
'useAudio() ref to <audio> element is empty at mount. ' +
155+
'useAudio() ref to <audio> element is empty at mount. ' +
156156
'It seem you have not rendered the audio element, which is ' +
157157
'returns as the first argument const [audio] = useAudio(...).'
158158
);
159159
}
160160
return;
161161
}
162-
162+
163163
// Start media, if autoPlay requested.
164164
if (props.autoPlay && el.paused) {
165165
controls.play();

src/useGetSetState.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {useRef} from './react';
2+
import useUpdate from './useUpdate';
3+
4+
const useGetSetState = <T extends object>(initialState: T = {} as T): [() => T, (patch: Partial<T>) => void]=> {
5+
if (process.env.NODE_ENV !== 'production') {
6+
if (typeof initialState !== 'object') {
7+
throw new TypeError('useGetSetState initial state must be an object.');
8+
}
9+
}
10+
11+
const update = useUpdate();
12+
const state = useRef<T>({...(initialState as object)} as T);
13+
const get = () => state.current;
14+
const set = (patch: Partial<T>) => {
15+
if (!patch) return;
16+
if (process.env.NODE_ENV !== 'production') {
17+
if (typeof patch !== 'object') {
18+
throw new TypeError('useGetSetState setter patch must be an object.');
19+
}
20+
}
21+
Object.assign(state.current, patch);
22+
update();
23+
};
24+
25+
return [get, set];
26+
};
27+
28+
export default useGetSetState;

0 commit comments

Comments
 (0)