Skip to content

Commit d01f765

Browse files
authored
Merge pull request #37 from streamich/adopt
Adopt
2 parents 5fe7925 + 91bee9a commit d01f765

File tree

8 files changed

+183
-0
lines changed

8 files changed

+183
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,12 @@
7777
<br/>
7878
- [**State**](./docs/State.md)
7979
- [`createMemo`](./docs/createMemo.md) &mdash; factory of memoized hooks.
80+
- [`useAdopt`](./docs/useAdopt.md) &mdash; extract value from multiple render-prop (or FaCC) components.
8081
- [`useCallbag`](./docs/useCallbag.md) &mdash; tracks latest value of a callbag.
8182
- [`useGetSet`](./docs/useGetSet.md) &mdash; returns state getter `get()` instead of raw state.
8283
- [`useGetSetState`](./docs/useGetSetState.md) &mdash; as if [`useGetSet`](./docs/useGetSet.md) and [`useSetState`](./docs/useSetState.md) had a baby.
8384
- [`useObservable`](./docs/useObservable.md) &mdash; tracks latest value of an `Observable`.
85+
- [`useRenderProp`](./docs/useRenderProp.md) &mdash; extracts value from a render-prop or a FaCC.
8486
- [`useSetState`](./docs/useSetState.md) &mdash; creates `setState` method which works like `this.setState`. [![][img-demo]](https://codesandbox.io/s/n75zqn1xp0)
8587
- [`useToggle` and `useBoolean`](./docs/useToggle.md) &mdash; tracks state of a boolean.
8688
- [`useCounter` and `useNumber`](./docs/useCounter.md) &mdash; tracks state of a number.

docs/useAdopt.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# `useAdopt`
2+
3+
Extracts a values from multiple render-prop or FaCC components.
4+
This hook is similar to [`useRenderProp`](./useRenderProp.md), but
5+
it allows to specify a named map of multiple render-prop elements
6+
from which to extract values.
7+
8+
9+
## Usage
10+
11+
```jsx
12+
import {useAdopt} from 'react-use';
13+
14+
const FaCC = ({children}) => {
15+
return children('VALUE-FaCC');
16+
};
17+
const RenderProp = ({render}) => {
18+
return render('VALUE-RenderProp');
19+
};
20+
21+
const Demo = () => {
22+
const [fragment, result] = useAdopt({
23+
facc: <FaCC/>,
24+
renderProp: <RenderProp/>,
25+
});
26+
27+
return (
28+
<>
29+
{fragment}
30+
<div>FaCC: {result.facc[0]}</div>
31+
<div>Render prop: {result.renderProp[0]}</div>
32+
</>
33+
);
34+
};
35+
```

docs/useRenderProp.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# `useRenderProp`
2+
3+
Extracts a value from render-prop or FaCC component.
4+
5+
6+
## Usage
7+
8+
```jsx
9+
import {useRenderProp} from 'react-use';
10+
11+
const FaCC = ({children}) => {
12+
return children('VALUE-FaCC');
13+
};
14+
const RenderProp = ({render}) => {
15+
return render('VALUE-RenderProp');
16+
};
17+
18+
const Demo = () => {
19+
const [fragment1, [value1]] = useRenderProp(<FaCC />);
20+
const [fragment2, [value2]] = useRenderProp(<RenderProp />);
21+
22+
return (
23+
<>
24+
{fragment1}
25+
{fragment2}
26+
<div>FaCC: {value1}</div>
27+
<div>Render prop: {value2}</div>
28+
</>
29+
);
30+
};
31+
```

src/__stories__/useAdopt.story.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {storiesOf} from '@storybook/react';
2+
import * as React from 'react';
3+
import {useAdopt} from '..';
4+
import ShowDocs from '../util/ShowDocs';
5+
6+
const FaCC = ({children}) => {
7+
return children('VALUE-FaCC');
8+
};
9+
const RenderProp = ({render}) => {
10+
return render('VALUE-RenderProp');
11+
};
12+
13+
const Demo = () => {
14+
const [fragment, result] = useAdopt({
15+
facc: <FaCC/>,
16+
renderProp: <RenderProp/>,
17+
});
18+
19+
return (
20+
<>
21+
{fragment}
22+
<div>FaCC: {result.facc[0]}</div>
23+
<div>Render prop: {result.renderProp[0]}</div>
24+
</>
25+
);
26+
};
27+
28+
storiesOf('useAdopt', module)
29+
.add('Docs', () => <ShowDocs md={require('../../docs/useAdopt.md')} />)
30+
.add('Demo', () =>
31+
<Demo/>
32+
)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import {storiesOf} from '@storybook/react';
2+
import * as React from 'react';
3+
import {useRenderProp} from '..';
4+
import ShowDocs from '../util/ShowDocs';
5+
6+
const FaCC = ({children}) => {
7+
return children('VALUE-FaCC');
8+
};
9+
const RenderProp = ({render}) => {
10+
return render('VALUE-RenderProp');
11+
};
12+
13+
const Demo = () => {
14+
const [fragment1, [value1]] = useRenderProp(<FaCC />);
15+
const [fragment2, [value2]] = useRenderProp(<RenderProp />);
16+
17+
return (
18+
<>
19+
{fragment1}
20+
{fragment2}
21+
<div>FaCC: {value1}</div>
22+
<div>Render prop: {value2}</div>
23+
</>
24+
);
25+
};
26+
27+
storiesOf('useRenderProp', module)
28+
.add('Docs', () => <ShowDocs md={require('../../docs/useRenderProp.md')} />)
29+
.add('Demo', () =>
30+
<Demo/>
31+
)

src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import createMemo from './createMemo';
2+
import useAdopt from './useAdopt';
23
import useAsync from './useAsync';
34
import useAudio from './useAudio';
45
import useBattery from './useBattery';
@@ -28,6 +29,7 @@ import useObservable from './useObservable';
2829
import useOrientation from './useOrientation';
2930
import useOutsideClick from './useOutsideClick';
3031
import useRaf from './useRaf';
32+
import useRenderProp from './useRenderProp';
3133
import useSetState from './useSetState';
3234
import useSize from './useSize';
3335
import useSpeech from './useSpeech';
@@ -43,6 +45,7 @@ import useWindowSize from './useWindowSize';
4345

4446
export {
4547
createMemo,
48+
useAdopt,
4649
useAsync,
4750
useAudio,
4851
useBattery,
@@ -72,6 +75,7 @@ export {
7275
useOrientation,
7376
useOutsideClick,
7477
useRaf,
78+
useRenderProp,
7579
useSetState,
7680
useSize,
7781
useSpeech,

src/useAdopt.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as React from 'react';
2+
import useRenderProp from './useRenderProp';
3+
4+
const useAdopt = <T extends {[key: string]: any[]}>(map: {[key in keyof T]: React.ReactElement<any>}): [React.ReactElement<any>, T] => {
5+
const keys = Object.keys(map);
6+
const fragments: React.ReactElement<any>[] = [];
7+
const result: T = {} as T;
8+
9+
keys.sort();
10+
for (const key of keys) {
11+
const [fragment, value] = useRenderProp(map[key]);
12+
fragments.push(React.cloneElement(fragment, {key}));
13+
result[key] = value;
14+
}
15+
16+
return [React.createElement(React.Fragment, null, ...fragments), result];
17+
};
18+
19+
export default useAdopt;

src/useRenderProp.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as React from 'react';
2+
import {useState, useCallback} from './react';
3+
import createMemo from './createMemo';
4+
5+
const useRenderProp = (element: React.ReactElement<any>): [React.ReactElement<any>, any[]] => {
6+
if (process.env.NODE_ENV !== 'production') {
7+
if (!React.isValidElement(element)) {
8+
throw new TypeError(
9+
'useRenderProp element to be a valid React element ' +
10+
'such as <MyRenderProp />.'
11+
);
12+
}
13+
}
14+
15+
const [state, setState] = useState<any[]>([]);
16+
const useSetState = createMemo((...args) => setState(args));
17+
const render = useCallback((...args) => {
18+
useSetState(...args);
19+
return null;
20+
}, []);
21+
const cloned = React.cloneElement(element, {
22+
render,
23+
children: render,
24+
});
25+
26+
return [cloned, state];
27+
};
28+
29+
export default useRenderProp;

0 commit comments

Comments
 (0)