Code Monkey home page Code Monkey logo

render-props's Introduction

render-props

Build Status npm version Coverage Status

TL;DR

  • This package is for component authors.
  • It allows you to easily and reliably support Render Props, Component Injection, AND Function as a Child.
  • Get increased performance from your SFCs while respecting the component's defaultProps.

Install

$ npm i --save render-props

API

Simply import from render-props using ES6.

import renderProps from 'render-props';

This will import the renderProps function which takes two parameters.

renderProps(componentOrFunction, props)
  • componentOrFunction - The first parameter is whatever was passed to your component in this.props.render (or whatever you call your prop).

  • props - The second parameter is a props object. This will be spread to the function or component.

Usage

Let's say that you authored or are authoring a component that takes a render prop (Render Props) or a Component (Component Injection). There is some overhead required to support both.

This package will allow you to support both Render Props and Component Injection with zero effort.

What does this package offer over doing the work yourself?

If you support Component Injection, this package will check to see if the component is a Stateless Functional Component (SFC) and, if so, will call it directly for improved performance.

Another benefit is that Render Props can now have defaultProps. Because, let's face it, a render prop is really just the same as a SFC.

Let's take the following component. It takes a prop named render. By simply importing the render-props package, you can now accept a function, a SFC, or a class component. Almost like magic! ๐ŸŽฉ

import renderProps from 'render-props';

class MyComponent extends Component {
  state = {};

  componentDidMount() {
    this.timer = setInterval(() => {
      const currentCount = this.state.count || 0;
      this.setState({ count: currentCount + 1 });
    }, 5000);
  }

  componentWillUnmount() {
    clearInterval(this.timer);
  }

  render() {
    return renderProps(this.props.render, this.state);
  }
}

You can use any of the following and they will all render properly.

const RenderCountSFC = ({ count, foo }) => ( 
  `Count = ${count} foo=${foo}`
);
RenderCountSFC.defaultProps = {
  foo: 'foo',
  count: 0,
};

class RenderCount extends Component {
  render() {
    const { count, foo } = this.props;
    return (
      `Count = ${count} foo=${foo}`
    );
  }
}
RenderCount.defaultProps = {
  foo: 'foo',
  count: 0,
};

const App = () => (
  <div>
    <h2>Traditional Render Prop</h2>
    <MyComponent
      render={
        ({ count, foo }) => (`Count = ${count} foo=${foo}`)
      }
    />

    <h2>Component Injection (SFC)</h2>
    <MyComponent render={RenderCountSFC} />

    <h2>Using Component Injection (class)</h2>
    <MyComponent render={RenderCount} />
  </div>
);

This will work no matter what you pass in the render prop. You can pass a function, a Stateless Functional Component (SFC), or a class component. In any case, it will be called to do the rendering.

Plus, if you pass a SFC, it will be rendered by calling it directly. This is a huge performance boost over using JSX/React.createElement.

[Note: Starting with version 1.1.0, SFCs that specify propTypes will be rendered as Components in order to take advantage of prop validation.]

This helper will also merge in any defaultProps that your component might be using.

Support Function as a Child too!

If you would also like to support Function as a Child you can change the example code above like this.

render() {
  const { children, render = children } = this.props;
  return renderProps(render, this.state);
}

It's good to note that I appall this pattern but I've shown the example for completeness.

See it Live!

Here is a CodeSandbox with the sample component shown above running live.

render-props's People

Contributors

cmccormack avatar donavon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

render-props's Issues

Fix CI

Fix CircleCI, or just switch to Travis (as I have a lot more experience with that anyway).

Handling anything (not just component or function)

I'm not 100% sure, but when using the "children" mode:

<foo>
   Hello
</foo>

with a render like that:

const content = this.props.children || this.props.render
return renderProps(content, this.props)

It fails pitifully with Cannot read property 'render' of undefined.

My guess is that's coming from the component detection in render-props.

Could we make it work?

CJS output includes `const`

It looks like the output for CJS isn't ready for browsers that don't support modules.

The build tool/step needs to be updated to convert newer syntax like const into alternatives that older browsers support

Ignored static properties of SFCs (Stateless Functional Components)

This library respects the use of defaultProps on SFCs, which is good (although in practice you can often just use default argument values for SFCs), but there are other properties an SFC can have that are currently ignored:

  • propTypes
  • contextTypes
  • displayName

I realize that calling a function directly is more efficient than mounting it as a component [1], so skipping displayName makes sense, but IMO propTypes and contextTypes should still be respected, if they exist. So in those cases I would think it would be better to go ahead and render the SFC as a component rather than calling it directly.

Example of PropTypes being ignored:

function Test({ render }) {
    // this should trigger a warning since we're passsing a number and not a string
    return renderProps(render, { name: 123 })
}

function Greeting({ name }) {
    return <div>Hi, {name}</div>
}

Greeting.propTypes = {
    name: PropTypes.string.isRequired
}

export default class App extends Component {
    render() {
        return (
            <div>
                <Test render={Greeting} />
            </div>
        )
    }
}

[1] FWIW, SFCs have new optimizations applied to them in React 16 that make them faster than regular class components ("regular class" meaning React.Component without a shouldComponentUpdate method...as to SFC vs. React.PureComponent, it depends on the situation).

Update to support latest react features

I was using this in a project based on the latest create-react-app and I was getting issues with arrow functions not having a prototype, which was causing the library to throw. It would be better to use react-is to check if the ComponentOrFunction is renderable with React.createElement.

import React from 'react'
import * as ReactIs from 'react-is'

const renderProps = (ComponentOrFunction, props) =>
  ReactIs.isValidElementType(ComponentOrFunction)
    ? React.createElement(ComponentOrFunction, props)
    : ComponentOrFunction({
        ...(ComponentOrFunction.defaultProps || {}),
        ...props,
      })

export default renderProps

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.