Intro to
Higher Order Components
Joe Buza
Overview
-
Problem
-
Definition
-
Patterns
-
Pros/Cons
-
Example
Problem
class AwesomeButton extends React.Component {
componentWillMount() {
const startTime = Date.now();
this.setState({ startTime });
console.log('Mounting ', startTime);
}
componentDidMount() {
const endTime = Date.now();
console.log('Mounted ', endTime);
console.log('Total Time ', endTime - this.state.startTime);
}
render() {
return <button>Sick button</button>;
}
}
Solution
const monitor = (Component) =>
class extends React.Component {
componentWillMount() {
const startTime = Date.now();
this.setState({ startTime });
log(`${Component.name} MOUNTING ${startTime}`);
}
componentDidMount() {
const endTime = Date.now();
log(`${Component.name} MOUNTED ${endTime}`);
log(`${Component.name} TOTAL TIME ${endTime - this.state.startTime}`);
}
render() {
return <Component {...this.props} />;
}
}
Usage
@monitor
class AwesomeButton extends React.Component {
render() {
return (
<button>Sick button</button>
);
}
}
class AwesomeButton extends React.Component {
render() {
return (
<button>Wow this is a great button</button>
);
}
}
const WrappedButton = monitor(AwesomeButton);
Or
What are HOCs?
A function that takes in a component and returns an enhanced component
Patterns
-
Props Proxy
-
Inheritance Inversion
Props Proxy
-
Manipulate props
-
Abstracting state
-
Wrap with other elements
function ppHOC(WrappedComponent) {
return class PP extends React.Component {
render() {
return <WrappedComponent {...this.props}/>
}
}
}
const makeUpperCase = (WrappedComponent) => {
return class UpperCaseComponent extends React.Component {
render() {
const props = Object.assign({}, this.props, {
title: this.props.title.toUpperCase()
});
return <WrappedComponent { ...props } />
}
};
}
Manipulating Props
const UpperCaseComponent = makeUpperCase(BaseComponent)
const makeToggleable = WrappedComponent => {
return class ToggleableComponent extends React.Component {
state = { toggled: false }
toggle = () => {
this.setState({ toggled: !this.state.toggled })
}
render() {
const props = { ...this.props, toggledOn: this.state.toggled }
return <WrappedComponent {...props} onClick={this.toggle} />
}
}
}
Abstracting State
const ToggleableComponent = makeToggleable(BaseComponent)
function withRedTheme(WrappedComponent) {
return class extends React.Component {
render() {
return (
<div style={{display: 'block', background: 'red'}}>
<WrappedComponent {...this.props}/>
</div>
)
}
}
}
Wrapping other elements
Inheritance Inversion
-
Render highjacking
-
Modifying Tree
-
Manipulating State
function iiHOC(WrappedComponent) {
return class Enhancer extends WrappedComponent {
render() {
return super.render()
}
}
}
function withPermission(WrappedComponent) {
return class Permission extends WrappedComponent {
render() {
if (this.props.loggedIn) {
return super.render()
} else {
return null
}
}
}
}
Render Highjacking
function iiHOC(WrappedComponent) {
return class Enhancer extends WrappedComponent {
render() {
const elementsTree = super.render()
let newProps = {}
if (elementsTree && elementsTree.type === 'input') {
newProps = { value: 'may the force be with you' }
}
const props = Object.assign({}, elementsTree.props, newProps)
const newElementsTree = React.cloneElement(
elementsTree,
props,
elementsTree.props.children
)
return newElementsTree
}
}
}
Modifying Tree
export function IIHOCDEBUGGER(WrappedComponent) {
return class II extends WrappedComponent {
render() {
return (
<div>
<h2>HOC Debugger Component</h2>
<p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre>
<p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre>
{super.render()}
</div>
)
}
}
}
Manipulate State
Pros
-
DRYer code
-
Modular code
-
Functional programming
Cons
-
Overriding props
-
Order matters
-
Maintainability
import React, {Component} from 'react'
import userStore from './UserStore'
const renderIf = predicateFn => (WrappedComponent, failFn) =>
class RenderIfComponent extends Component {
render() {
return predicateFn(userStore.modules) ? (
<WrappedComponent {...this.props} />
) : (
failFn ? failFn(this.props) : null
)
}
}
export default renderIf
How could we use HOC?
import React, {Component} from 'react'
import userStore from './UserStore'
export default class RenderIf extends Component {
render() {
const {predicateFn, children} = this.props
return children(predicateFn(userStore.modules))
}
}
Resources
-
https://medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e#.u8zqi15sj
-
https://www.youtube.com/watch?v=BcVAq3YFiuc&list=WL&index=1
-
http://blog.scottlogic.com/2016/09/20/the-power-of-the-higher-order.html
-
https://spin.atomicobject.com/2017/03/02/higher-order-components-in-react/
Intro to Higher Order Components
By Joe Buza
Intro to Higher Order Components
This presentation will explain how higher order components will make your code DRYer and more modular.
- 1,186