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;