From 2d85c61ba8651e447960c6987c3fbfc1031af4b6 Mon Sep 17 00:00:00 2001 From: streamich Date: Mon, 29 Oct 2018 22:45:09 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20useRenderProp=20ho?= =?UTF-8?q?ok?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + docs/useRenderProp.md | 31 +++++++++++++++++++++++++ src/__stories__/useRenderProp.story.tsx | 31 +++++++++++++++++++++++++ src/index.ts | 2 ++ src/useRenderProp.ts | 29 +++++++++++++++++++++++ 5 files changed, 94 insertions(+) create mode 100644 docs/useRenderProp.md create mode 100644 src/__stories__/useRenderProp.story.tsx create mode 100644 src/useRenderProp.ts diff --git a/README.md b/README.md index 8f5af6c5..2346e1a4 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/docs/useRenderProp.md b/docs/useRenderProp.md new file mode 100644 index 00000000..0555d81a --- /dev/null +++ b/docs/useRenderProp.md @@ -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(); + const [fragment2, [value2]] = useRenderProp(); + + return ( + <> + {fragment1} + {fragment2} +
FaCC: {value1}
+
Render prop: {value2}
+ + ); +}; +``` diff --git a/src/__stories__/useRenderProp.story.tsx b/src/__stories__/useRenderProp.story.tsx new file mode 100644 index 00000000..23dca3b4 --- /dev/null +++ b/src/__stories__/useRenderProp.story.tsx @@ -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(); + const [fragment2, [value2]] = useRenderProp(); + + return ( + <> + {fragment1} + {fragment2} +
FaCC: {value1}
+
Render prop: {value2}
+ + ); +}; + +storiesOf('useRenderProp', module) + .add('Docs', () => ) + .add('Demo', () => + + ) diff --git a/src/index.ts b/src/index.ts index 68668652..6cbb5f6a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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, diff --git a/src/useRenderProp.ts b/src/useRenderProp.ts new file mode 100644 index 00000000..53b34446 --- /dev/null +++ b/src/useRenderProp.ts @@ -0,0 +1,29 @@ +import * as React from 'react'; +import {useState, useCallback} from './react'; +import createMemo from './createMemo'; + +const useRenderProp = (element: React.ReactElement): [React.ReactElement, any[]] => { + if (process.env.NODE_ENV !== 'production') { + if (!React.isValidElement(element)) { + throw new TypeError( + 'useRenderProp element to be a valid React element ' + + 'such as .' + ); + } + } + + const [state, setState] = useState([]); + 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;