Higher Order Components in React

By Sarah Herr

Overview

  • Definition
  • Best practices
  • Patterns
  • Example

What are higher order components?

Higher order components (HOCs) are components in React that take a component as an argument and return an enhanced component

Let's look at an example

const CartHeader = (props) => {
  if (props.user) {
    return <p>Welcome, {props.user.name}</p>
  } else {
    return <p>Log in to checkout</p>
  }
}

The component to wrap:

const CartHeaderwithUser = withUser(CartHeader);

Implementing the HOC:

function withUser(Component) {
  const userInfo = {
    name: "Sarah Herr",
    address: "123 W 14th St New York, NY",
    cardNo: 123456789,
    cardExp: 0419
  };

  return function(props) {
    return <Component user={userInfo} {...props} />
  }
}

The higher order component:

HOCs should be pure functions

  • No side effects

 

  • All data passed in as arguments

 

  • Predictable 

HOCs are a type of container component

  • The original component is composed by wrapping it in the HOC

 

  • A HOC should pass through props that are unrelated to its specific concern 

HOC Patterns

  1. Manipulate props
  2. Inheritance inversion

Pattern 1: props

Uses:

  • Manipulate props

  • Provide props and callbacks

  • Wrapping with elements

example

function withStyle(Component) {
  return class fancyComponent extends React.Component {
    render() {
      return (
        <div className="sassy" style={{display: 'block'}}>
          <Component {...this.props} />
        </div>
      )
    }
  }
}

Pattern 2: inheritance inversion

Uses:

  • Render highjacking

  • Manipulating State

example

function loggedIn(MessagesComponent) {
  return class Conditional extends MessagesComponent {
    render() {
      if (this.props.loggedIn) {
        return super.render()
      } else {
        return null
      }
    }
  }
}

Example: Making a Form

Without a HOC

class Cat extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: '',
      color: '',
      food: ''
    }

    this.handleName = this.handleName.bind(this);
    this.handleColor = this.handleColor.bind(this);
    this.handleFood = this.handleFood.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleName(evt) {
    evt.preventDefault();
    this.setState({
      name: evt.target.value
    })
  }

  handleColor(evt) {
    evt.preventDefault();
    this.setState({
      color: evt.target.value
    })
  }

  handleFood(evt) {
    evt.preventDefault();
    this.setState({
      food: evt.target.value
    })
  }

  handleSubmit(evt) {
    store.dispatch(addCat(this.state))
  }

  render() {
    return (
      <div>
        <form onSubmit={this.handleSubmit}>
          <label>Name:</label>
          <input onChange={this.handleName} />

          <label>Color:</label>
          <input onChange={this.handleColor} />

          <label>Food:</label>
          <input onChange={this.handleFood} />

          <button type='submit'>Add Cat</button>
        </form>
      </div>
    )
  }
}

Making the HOC

function withForm(WrappedComponent, actionCreator) {
  return class extends React.Component {
    constructor(props) {
      super(props);				

      this.handleInput = this.handleInput.bind(this);
      this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleInput(evt) {
      evt.preventDefault();
      this.setState({ 
        evt.target.name: evt.target.value 
      })
    }

    handleSubmit(evt) {
      store.dispatch(actionCreator(this.state))
    }

    render() {
      const formProps = {
        handleInput: this.handleInput,
        handleSubmit: this.handleSubmit
      }

      return <WrappedComponent form={formProps} {…this.props} />
    }
  }
}

Refactoring the Component

const Cat = (props) => {
  return (
    <div>
      <form onSubmit={props.form.handleSubmit}>
        <label>Name:</label>
        <input name="name" onChange={props.form.handleInput} />

        <label>Color:</label>
        <input name="color" onChange={props.form.handleInput} />

        <label>Food:</label>
        <input name="food" onChange={props.form.handleInput} />

        <button type='submit'>Add Cat</button>
      </form>
    </div>
  )
}

Wrapping a Component

import { addCat } from '../action-creators/animals'

const CatWithForm = withForm(Cat, addCat)

Now we can re-use the HOC

import { addUnicorn } from '../action-creators/animals'
import { withForm } from '../higher-order-components'

const UnicornWithForm = withForm(Unicorn, addUnicorn)

const Unicorn = (props) => {
  return (
    <div>
      <form onSubmit={props.form.handleSubmit}>
        <label>Name:</label>
        <input name="name" onChange={props.form.handleInput} />

        <label>Horn length:</label>
        <input name="horn" onChange={props.form.handleInput} />

        <label>Sparkle Color:</label>
        <input name="sparkle" onChange={props.form.handleInput} />

        <label>Favorite Cloud:</label>
        <input name="cloud" onChange={props.form.handleInput} />

        <button type='submit'>Add Unicorn</button>
      </form>
    </div>
  )
}

In COnclusion

  • DRY-er Code

 

  • Creates more modular code

 

  • Emphasizes functional programming

Higher Order Components in React

By sarahdherr

Higher Order Components in React

  • 718