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
- Manipulate props
- 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