feat: 🎸 add useRenderProp hook

This commit is contained in:
streamich 2018-10-29 22:45:09 +01:00
parent 5fe7925cf0
commit 2d85c61ba8
5 changed files with 94 additions and 0 deletions

View File

@ -81,6 +81,7 @@
- [`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.
- [`useObservable`](./docs/useObservable.md) — tracks latest value of an `Observable`.
- [`useRenderProp`](./docs/useRenderProp.md) — extracts value from a render-prop or a FaCC.
- [`useSetState`](./docs/useSetState.md) — creates `setState` method which works like `this.setState`. [![][img-demo]](https://codesandbox.io/s/n75zqn1xp0)
- [`useToggle` and `useBoolean`](./docs/useToggle.md) — tracks state of a boolean.
- [`useCounter` and `useNumber`](./docs/useCounter.md) — tracks state of a number.

31
docs/useRenderProp.md Normal file
View File

@ -0,0 +1,31 @@
# `useRenderProp`
Extracts a value from render-prop or FaCC component.
## Usage
```jsx
import {useRenderProp} from 'react-use';
const FaCC = ({children}) => {
return children('VALUE-FaCC');
};
const RenderProp = ({render}) => {
return render('VALUE-RenderProp');
};
const Demo = () => {
const [fragment1, [value1]] = useRenderProp(<FaCC />);
const [fragment2, [value2]] = useRenderProp(<RenderProp />);
return (
<>
{fragment1}
{fragment2}
<div>FaCC: {value1}</div>
<div>Render prop: {value2}</div>
</>
);
};
```

View File

@ -0,0 +1,31 @@
import {storiesOf} from '@storybook/react';
import * as React from 'react';
import {useRenderProp} from '..';
import ShowDocs from '../util/ShowDocs';
const FaCC = ({children}) => {
return children('VALUE-FaCC');
};
const RenderProp = ({render}) => {
return render('VALUE-RenderProp');
};
const Demo = () => {
const [fragment1, [value1]] = useRenderProp(<FaCC />);
const [fragment2, [value2]] = useRenderProp(<RenderProp />);
return (
<>
{fragment1}
{fragment2}
<div>FaCC: {value1}</div>
<div>Render prop: {value2}</div>
</>
);
};
storiesOf('useRenderProp', module)
.add('Docs', () => <ShowDocs md={require('../../docs/useRenderProp.md')} />)
.add('Demo', () =>
<Demo/>
)

View File

@ -28,6 +28,7 @@ import useObservable from './useObservable';
import useOrientation from './useOrientation';
import useOutsideClick from './useOutsideClick';
import useRaf from './useRaf';
import useRenderProp from './useRenderProp';
import useSetState from './useSetState';
import useSize from './useSize';
import useSpeech from './useSpeech';
@ -72,6 +73,7 @@ export {
useOrientation,
useOutsideClick,
useRaf,
useRenderProp,
useSetState,
useSize,
useSpeech,

29
src/useRenderProp.ts Normal file
View File

@ -0,0 +1,29 @@
import * as React from 'react';
import {useState, useCallback} from './react';
import createMemo from './createMemo';
const useRenderProp = (element: React.ReactElement<any>): [React.ReactElement<any>, any[]] => {
if (process.env.NODE_ENV !== 'production') {
if (!React.isValidElement(element)) {
throw new TypeError(
'useRenderProp element to be a valid React element ' +
'such as <MyRenderProp />.'
);
}
}
const [state, setState] = useState<any[]>([]);
const useSetState = createMemo((...args) => setState(args));
const render = useCallback((...args) => {
useSetState(...args);
return null;
}, []);
const cloned = React.cloneElement(element, {
render,
children: render,
});
return [cloned, state];
};
export default useRenderProp;