Skip to content

Commit

Permalink
feat: 🎸 add useGetSetState
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Oct 29, 2018
1 parent efa9acd commit dcd1013
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 6 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
23 changes: 23 additions & 0 deletions docs/useGetSetState.md
Original file line number Diff line number Diff line change
@@ -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>
);
};
```
23 changes: 23 additions & 0 deletions src/__stories__/useGetSetState.story.tsx
Original file line number Diff line number Diff line change
@@ -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/>
)
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -45,6 +46,7 @@ export {
useFavicon,
useGeolocation,
useGetSet,
useGetSetState,
useHover,
useIdle,
useLifecycles,
Expand Down
12 changes: 6 additions & 6 deletions src/useAudio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ 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({
muted: el.muted,
volume: el.volume,
});
};
const onDurationChange = (event) => {
const onDurationChange = () => {
const el = ref.current;
if (!el) return;
const {duration, buffered} = el;
Expand All @@ -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)});
Expand Down Expand Up @@ -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 = () => {
Expand Down Expand Up @@ -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();
Expand Down
28 changes: 28 additions & 0 deletions src/useGetSetState.ts
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit dcd1013

Please sign in to comment.