Skip to content

Commit 03bba16

Browse files
committed
feat: allow to use React.cloneElement to pass props to child elements
1 parent 3ce1f44 commit 03bba16

File tree

4 files changed

+72
-20
lines changed

4 files changed

+72
-20
lines changed

.size-snapshot.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"dist/react-render-callback.cjs.js": {
3-
"bundled": 1296,
4-
"minified": 744,
5-
"gzipped": 404
3+
"bundled": 1491,
4+
"minified": 820,
5+
"gzipped": 431
66
},
77
"dist/react-render-callback.esm.js": {
8-
"bundled": 1150,
9-
"minified": 641,
10-
"gzipped": 354,
8+
"bundled": 1363,
9+
"minified": 733,
10+
"gzipped": 383,
1111
"treeshaked": {
1212
"rollup": {
1313
"code": 78,
@@ -19,13 +19,13 @@
1919
}
2020
},
2121
"dist/react-render-callback.umd.min.js": {
22-
"bundled": 6779,
23-
"minified": 3110,
24-
"gzipped": 1094
22+
"bundled": 6986,
23+
"minified": 3178,
24+
"gzipped": 1118
2525
},
2626
"dist/react-render-callback.umd.js": {
27-
"bundled": 11294,
28-
"minified": 4707,
29-
"gzipped": 1354
27+
"bundled": 11501,
28+
"minified": 4775,
29+
"gzipped": 1379
3030
}
3131
}

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ npm install --save react-render-callback
8282

8383
### API
8484

85-
`render([ renderable [, props ] ])`:
85+
`render([ renderable [, props [, options ] ] ])`:
8686

8787
- `renderable` (optional): anything that can be rendered like a function, a component, or elements
8888
- invokes stateless function components (SFC) respecting their
@@ -101,6 +101,18 @@ npm install --save react-render-callback
101101
- gracefully handles other types like string, array,
102102
[react elements][create-element], ...
103103
- `props` (optional): to pass to `renderable` (if renderable is a function or react element type)
104+
- `options` (optional):
105+
106+
- `cloneElement` (optional, default: `false`): allows to pass props to
107+
the element using [`React.cloneElement`][clone-element]
108+
109+
```js
110+
render(<a href="#bar">bar</a>, {title: 'foo'})
111+
// --> <a href="#bar">bar</a>
112+
113+
render(<a href="#bar">bar</a>, {title: 'foo'}, {cloneElement: true})
114+
// --> <a href="#bar" title="foo">bar</a>
115+
```
104116

105117
**returns**
106118

@@ -224,6 +236,7 @@ MIT
224236
[function-as-children]: https://reactpatterns.com/#function-as-children
225237
[react-component]: https://reactjs.org/docs/react-component.html
226238
[create-element]: https://reactjs.org/docs/react-api.html#createelement
239+
[clone-element]: https://reactjs.org/docs/react-api.html#cloneelement
227240
[typechecking-with-proptypes]: https://reactjs.org/docs/typechecking-with-proptypes.html
228241
[prop-types]: https://www.npmjs.com/package/prop-types
229242
[react-is]: https://www.npmjs.com/package/react-is

src/render.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {createElement} from 'react'
1+
import {isValidElement, createElement, cloneElement} from 'react'
22

33
import isReactComponent from './internal/isReactComponent'
44

@@ -8,17 +8,29 @@ const isFalsy = value =>
88
value === '' ||
99
(typeof value === 'number' && isNaN(value))
1010

11-
const result = (maybeFunction, props) =>
12-
typeof maybeFunction === 'function'
13-
? maybeFunction({...maybeFunction.defaultProps, ...props})
14-
: maybeFunction // must be something else
11+
const result = (maybeFunction, props, options) => {
12+
if (typeof maybeFunction === 'function') {
13+
return maybeFunction({...maybeFunction.defaultProps, ...props})
14+
}
15+
16+
if (
17+
options &&
18+
options.cloneElement &&
19+
props &&
20+
isValidElement(maybeFunction)
21+
) {
22+
return cloneElement(maybeFunction, props)
23+
}
24+
25+
return maybeFunction // must be something else
26+
}
1527

16-
export default (renderable, props) => {
28+
export default (renderable, props, options) => {
1729
if (isReactComponent(renderable)) {
1830
return createElement(renderable, props)
1931
}
2032

21-
const element = result(renderable, props)
33+
const element = result(renderable, props, options)
2234

2335
return isFalsy(element) ? null : element
2436
}

src/render.test.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,33 @@ describe('render([renderable[, props]])', () => {
2727
expect(render(renderable)).toBe(renderable)
2828
})
2929

30+
it('should clone element providing additional props', () => {
31+
const renderable = <a href="#bar">bar</a>
32+
expect(render(renderable, {title: 'foo'}, {cloneElement: true}))
33+
.toMatchInlineSnapshot(`
34+
<a
35+
href="#bar"
36+
title="foo"
37+
>
38+
bar
39+
</a>
40+
`)
41+
})
42+
43+
it('should not clone element by default', () => {
44+
const renderable = <a href="#bar">bar</a>
45+
46+
expect(render(renderable, {title: 'foo'})).toBe(renderable)
47+
48+
expect(render(renderable, {title: 'foo'})).toMatchInlineSnapshot(`
49+
<a
50+
href="#bar"
51+
>
52+
bar
53+
</a>
54+
`)
55+
})
56+
3057
describe('should return null for', () => {
3158
;[false, null, undefined, NaN, ''].forEach(value => {
3259
it(`${value}`, () => {

0 commit comments

Comments
 (0)