Codementor Events

Reusing React Component logic with Render Props

Published Sep 24, 2018

If you have used React for some time, Reusing Components is one of the things you would always do. The most obvious way most of us do is using Higher Order Component.

Having read through Chinonso Johnson’s article on Reusing React component logic with HOC. I thought about achieving the same result but using Render props instead. This is what we will cover in this post.

Reusing React Component logic with Higher Order Component
_A Higher Order Component is a great pattern that has proven to be very valuable. React emphasizes composition over…_medium.com

Render Props

According to Michael Jackson, a render prop is a function prop that a component uses to know what to render. By the way, if you have not watched Michael Jackson’s video on render props, please do.

To start, imagine we have a Signin and Signup Component both having the onChange event handler that changes the state

SignIn Component


import React from 'react';

class SignIn extends React.Component {
  state = {
    auth: {
      email: '',
      password: '',
    },
  };

  onChange = ({ target }) => {
    this.setState({
      auth: {
        ...this.state.auth,
        [target.name]: target.value,
      },
    });
  };

  onSubmit = (event) => {
    event.preventDefault();
    // do something here

    console.log(this.state.auth);
  };

  render() {
    const { email, password } = this.state.auth;
    return (
      <form onSubmit={this.onSubmit}>
        <h4> Sign In </h4>
        <input type="email" name="email" onChange={this.onChange} value={email} />
        <label htmlFor="email">Email</label>

        <input type="password" name="password" onChange={this.onChange} value={password} />
        <label htmlFor="password">Password</label>

        <button type="submit">Submit</button>
      </form>
    );
  }
}

export default SignIn;

Signup Component

import React from 'react';

class Signup extends React.Component {
  state = {
    auth: {
      email: '',
      password: '',
    },
  };

  onChange = ({ target }) => {
    this.setState({
      auth: {
        ...this.state.auth,
        [target.name]: target.value,
      },
    });
  };

  onSubmit = (event) => {
    event.preventDefault();
    // do something here

    console.log(this.state.auth);
  };

  render() {
    const { email, password, name, username } = this.state.auth;
    return (
      <form onSubmit={this.onSubmit}>
        <h4> Sign Up </h4>
        <label htmlFor="email">Name</label>
        <input type="text" name="name" onChange={this.onChange} value={name} />

        <label htmlFor="email">Username</label>
        <input type="text" name="username" onChange={this.onChange} value={username} />

        <label htmlFor="email">Email</label>
        <input type="email" name="email" onChange={this.onChange} value={email} />

        <input type="password" name="password" onChange={this.onChange} value={password} />
        <label htmlFor="password">Password</label>

        <button type="submit">Submit</button>
      </form>
    );
  }
}

export default Signup;

As you can see, repetitive onChange and onSubmit functions in both Signin and Signup components. We don’t want to do this, as it defeats the purpose of DRY (Don’t Repeat Yourself)

Now let’s create a reusable component that can be used to share this logic with both the Signin and Signup components.

Auth Render


import React, { Component } from 'react';

class authRender extends Component {
  state = { auth: {} };

  onChange = ({ target }) => {
    this.setState({
      auth: {
        ...this.state.auth,
        [target.name]: target.value,
      },
    });
  };

  onSubmit = (e) => {
    e.preventDefault();

    console.log(this.state.auth);
  };
  getStateAndHelpers = () => {
    return {
      onChange: this.onChange,
      onSubmit: this.onSubmit,
      auth: this.state.auth,
    };
  };

  render() {
    return this.props.children(this.getStateAndHelpers());
  }
}

export default authRender;

What we have done in the renderProps file is that we just took the logic from both Signin and Signup components into a separate component that simply renders the children. You can then pass all the props to be available inside the children function, although I decided to separate it into its own function. This approach is recommended by Kent C. Dodds

How do we use it

It is highly recommended to separate your components into Containers and Presentational Components. Basically, Containers have state either a component state or a state management like redux.

We are going to create a new file inside the container where it will have all the state and the event handlers.

Signin Container


import React, { Component } from 'react';
import AuthRender from './renderProps';
import Signin from './Signin';

class signinContainer extends Component {
  render() {
    return (
      <div>
       <Auth>{({ onChange, onSubmit }) => <Signin onChange={onChange} onSubmit={onSubmit} />}</Auth>
      </div>
    );
  }
}

Refactored Signin component


import React from 'react';

const SignIn = ({ onChange, onSubmit }) => (
  <form onSubmit={onSubmit}>
    <h4> Sign In </h4>
    		<label htmlFor="email">Email</label>
    <input type="email" name="email" onChange={onChange} className="validate" />
    		<label htmlFor="password">Password</label>
    <input type="password" name="password" onChange={onChange} className="validate" />
    <button type="submit">Submit</button>
  </form>
);

export default SignIn;

As we can see, our Signin component has been refactored into functional components making it easy to test. Of course, this is not the best use case for using render props, since there are other simpler ways of achieving the same without HOC or render props.

Benefits of using Render Props

We don’t have to wonder where our state or props are coming from. We can see them in the render prop’s argument list.

There is no automatic merging of property names, so there is no chance for a naming collision. Michael Jackson

This is true because imagine having multiple HOC on one component, you might be lost, making it difficult to know where the props are coming from.

Conclusion

Render props are amazing, it can do all H0C with a regular component. I encourage you to watch Michael ’s talk to understand it more and hope you will refactor a lot of your HOC to use regular component with render props.

Discover and read more posts from Terrence
get started