Skip to content

render-prop helper to render anything (Functions, Components, Elements, ...)

License

Notifications You must be signed in to change notification settings

sastan/react-render-callback

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

39 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

react-render-callback

render anything (Function as Child, Render Props, Components, Elements, ...)


version MIT License module formats: umd, cjs, and es umd size umd gzip size

Build Status Code Coverage Maintainability PRs Welcome Code of Conduct

Sponsored by Kenoxa Semver semantic-release Greenkeeper badge

The problem

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.

This solution

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:

View an example in codesandbox.io.

Table of Contents

Installation

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>

Usage

API

render([ renderable [, props [, options ] ] ])

renders the given renderable with props

// 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
  • props (optional): to pass to renderable (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 using React.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 and true are returned as null just like in JSX
  • the value as is for all other values

createRender([ renderable [, options ] ])

since: v1.1.0

Returns a function ((...args) => ...) to render renderable 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) => ...)

Examples

A basic example showing the can be viewed at codesandbox.io.

Use options.cloneElement

Edit

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>
)

Use createRender to pass down several arguments

Edit

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>
)

Use createRender to interop with a library which only supports function as render-prop

Edit

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>

Other Solutions

Credits

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.

Contributors

Thanks goes to these people (emoji key):


Sascha Tandel
πŸ’» πŸ“– πŸš‡ ⚠️ πŸ‘€ πŸ“ πŸ› πŸ’‘ πŸ€” πŸ“’

This project follows the all-contributors specification. Contributions of any kind welcome!

LICENSE

MIT