render anything (Function as Child, Render Props, Components, Elements, ...)
You want your component to support the render prop
pattern
and you want to support several different types of values like
Function as children,
a React.Component
or just plain react elements.
react-render-callback
frees you from detecting what kind of callback your component is dealing with:
import React from 'react'
import render from 'react-render-callback'
class Component from React.Component {
state = {}
render() {
// can be any prop: return render(this.props.renderHeader, this.state.header)
return render(this.props.children, this.state)
}
}
It can render the following types:
- Stateless Function Components (SFC)
with one argument (the common
props
case) aka Render Props Pattern or optional with several arguments - Class Components aka Component Injection Pattern
- Context Provider and Consumer
- Forward Refs
- Factories
- Elements with optional support for cloning to merge props
- primitives like strings, numbers, arrays, ...
false
,null
,undefined
andtrue
are returned asnull
just like in JSX
View an example in codesandbox.io.
This module is distributed via npm which is bundled with node and
should be installed as one of your project's dependencies
:
npm install --save react-render-callback
This package also depends on
react
. Please make sure you have it installed as well.
The Universal Module Definition (UMD) is available
via unpkg.com and exposed as ReactRenderCallback
.
<script src="https://unpkg.com/react-render-callback/dist/react-render-callback.umd.min.js"></script>
renders the given
renderable
withprops
// esm
import render from 'react-render-callback'
// commonjs
const render = require('react-render-callback')
renderable
(optional): anything that can be rendered like a function, a component, or elements- uses
React.createElement
for react types like class components, context provider or consumer, forward refs, factories, ... - invokes stateless function components (SFC) respecting their
defaultProps
- not using
React.createElement
for improved performance - except the SFC has
propTypes
andprocess.env.NODE_ENV
is notproduction
, in that caseReact.createElement
is used to enable typechecking with PropTypes
- not using
- gracefully handles other types like string, array, react elements, ...
- uses
props
(optional): to pass torenderable
(if renderable is a function or react element type)options
(optional):-
cloneElement
(default:false
, since: v1.1.0): allows to pass props to the element usingReact.cloneElement
render(<a href="#bar">bar</a>, {title: 'foo'}) // --> <a href="#bar">bar</a> render(<a href="#bar">bar</a>, {title: 'foo'}, {cloneElement: true}) // --> <a href="#bar" title="foo">bar</a>
-
returns
false
,null
,undefined
andtrue
are returned asnull
just like in JSX- the value as is for all other values
since: v1.1.0
Returns a function (
(...args) => ...
) to renderrenderable
with.
// esm
import {createRender} from 'react-render-callback'
// commonjs
const {createRender} = require('react-render-callback')
Accepts the same arguments (except props
) as render()
. It exists mainly
to pre-determine (read cache) what type renderable
is, to prevent these
checks on every invocation.
Additionally the returned method accepts more than one argument (since: v1.2.0). This allows to provide several parameters to the render function.
const render = createRender((a, b, c) => ({a, b, c}))
render(1, 2, 3)
// -> { a: 1, b: 2, c: 3 }
If only one argument is passed and it is a plain object, that argument is merged
with (if defined) the defaultProps
of the renderable
.
returns
a function ((...args) => ...
)
A basic example showing the can be viewed at codesandbox.io.
This option allows to pass down props without to need to create a function within render which merges the parent and received props.
class CountSeconds extends React.Component {
state = {
value: 0,
}
componentDidMount() {
this.timer = setInterval(() => {
this.setState(({value}) => ({value: value + 1}))
}, 1000)
}
componentWillUnmount() {
clearInterval(this.timer)
}
render() {
const {children, render = children} = this.props
return renderCallback(render, this.state, {cloneElement: true})
}
}
const DisplayValue = ({prefix = '', value}) => `${prefix}${value}`
const App = ({prefix}) => (
<CountSeconds>
<DisplayValue prefix={prefix} />
</CountSeconds>
)
class CountSeconds extends React.Component {
state = {
value: 0,
}
reset = () => {
this.setState({value: 0})
}
componentDidMount() {
this.timer = setInterval(() => {
this.setState(({value}) => ({value: value + 1}))
}, 1000)
}
componentWillUnmount() {
clearInterval(this.timer)
}
render() {
const {children, render = children} = this.props
return createRender(render)(this.state.value, this.reset)
}
}
const DisplayValue = ({prefix = '', value}) => `${prefix}${value}`
const App = () => (
<CountSeconds>
{(value, reset) => (
<React.Fragment>
<DisplayValue prefix="Seconds: " value={value} />
<button onClick={reset} type="button">
reset
</button>
</React.Fragment>
)}
</CountSeconds>
)
import Toggle from 'react-toggled'
const Toggler = ({on, getTogglerProps, onLabel, offLabel}) => (
<div>
<button {...getTogglerProps()}>Toggle me</button>
<div>{on ? onLabel : offLabel}</div>
</div>
)
Toggler.defaultProps = {
onLabel: 'Toggled On',
offLabel: 'Toggled Off',
}
const ToggleView = createRender(Toggler)
const App = () => <Toggle>{ToggleView}</Toggle>
A special thanks needs to go to Kent C. Dodds for his great video series ( egghead.io, frontendmasters.com and youtube.com). His projects are either used in this project (kcd-scripts) or are a template for the structure of this project (downshift). Make sure to subscribe to his newsletter.
Thanks goes to these people (emoji key):
Sascha Tandel π» π π |
---|
This project follows the all-contributors specification. Contributions of any kind welcome!
MIT