Kyle Shevlin
Front End JavaScript Developer building apps with React and Redux. Also a fan of Node, Rails, D3 and more.
function add (x, y) {
return x + y
}
const sum = add(add(4, 3), 2)
console.log(sum) // 9
"Functions as first class citizens" means you can pass function anywhere as if it's a variable.
A "higher order function" does one of the following:
function emojiLogs (emoji) {
return function (...strs) {
console.log(`${emoji} ${[...strs].join(' ')}`)
}
}
const catLog = emojiLog(😻)
const dogLog = emojiLog(🐶)
const logLog = emojiLog(🌲)
catLog('Krios', 'Tali')
// 😻 Krios Tali
dogLog('<- Why do I not have one of these yet?!')
// 🐶 <- Why do I not have one of these yet?!
logLog('Douglas Firs are real nice!')
// 🌲 Douglas Firs are real nice!
Currying is the technique of refactoring a function that normally accepts multiple arguments into one that accepts them one at a time.
A curried function will continue to return a unary function for each argument until the final one is provided. Then, the function is evaluated.
The arguments provided to a curried function are said to be "partially applied" while the function awaits its final argument.
// add : Number -> Number -> Number
const add = x => y => x + y
const add2 = add(2) // The number 2 is partially applied
const add5 = add(5) // The number 5 is partially applied
const eight = add5(3) // 8
console.log(add2(eight) // 10
If you remember high school math, composition is the act of passing the result of one function to another function.
This takes the form of `f(g(x))` where `f` and `g` are both functions. But nesting functions in JavaScript can be really ugly.
const scream = str => str.toUpperCase()
const exclaim = str => `${str}!`
const repeat = str => `${str} ${str}`
const warnSteve = repeat(exclaim(scream('Steve, the world is coming to an end')))
console.log(warnSteve)
// STEVE, THE WORLD IS COMING TO AN END! STEVE, THE WORLD IS COMING TO AN END!
Instead of nesting functions, what if we used currying and partial application to create a function that would do the composition for us?
// Pretend we imported `scream`, `exclaim`, and `repeat` from the previous slide
const compose = (...fns) => value =>
fns.reduceRight((accumulated, fn) => fn(accumulated), value)
const withGusto = compose(
repeat,
exclaim,
scream
)
const warnSteve = withGusto('Steve, the world is coming to an end')
console.log(warnSteve)
// STEVE, THE WORLD IS COMING TO AN END! STEVE, THE WORLD IS COMING TO AN END!
A higher order component is a function that takes a Component as one of its arguments and returns a new Component.
// identityHoc : Component A -> Object -> Component B
const identityHoc = BaseComponent => props => <BaseComponent {...props} />
// User : Object -> Component
const User = ({ name, age }) =>
<div className='user'>
<h3 className='user-name'>{name}</h3>
<div>Age: {age}</div>
</div>
const AlsoUser = identityHoc(User)
React.render((
<div>
// Both these will render identically
<User name='Kyle' age='32' />
<AlsoUser name='Kyle' age='32' />
</div>
), document.getElementById('app'))
The withState higher order component allows you to add state to SFCs which get passed as props to the component.
// withState(
// stateName: string,
// stateUpdaterName: string,
// initialState: any | (props: Object) => any
// ): HigherOrderComponent
const withToggle = withState('isToggled', 'setToggle', false)
The withHandlers higher order component allows you to add handler functions for updating state in SFCs. Also passed as to SFCs which get passed as props to the component.
// withHandlers(
// handlerCreators: {
// [handlerName: string]: (props: Object) => Function
// }
// ): HigherOrderComponent
const withToggleHandlers = withHandlers({
show: ({ toggle }) => () => toggle(true),
hide: ({ toggle }) => () => toggle(false),
toggle: ({ toggle }) => () => toggle(x => !x)
})
The withReducer
higher order component allows you to use a Redux-like reducer to handle state.
// withReducer<S, A>(
// stateName: string,
// dispatchName: string,
// reducer: (state: S, action: A) => S,
// initialState: S | (ownerProps: Object) => S
// ): HigherOrderComponent
const withToggleReducer = withReducer(
'isToggled',
'dispatch',
(state, action) => {
switch (action.type) {
case 'SHOW':
return true
case 'HIDE':
return false
case 'TOGGLE'
return !state
default:
return state
}
},
false
)
The lifecycle higher order component allows you to add all the lifecycle methods (except for render) to a SFC.
// lifecycle(
// spec: Object,
// ): HigherOrderComponent
const withData = lifecycle({
componentDidMount () {
fetch('data.json')
.then(response => response.json())
.then(data => this.setState({ data })
}
})
The pure higher order component returns a `PureComponent` version of your SFC.
💥
Next slide.
The mapProps higher order component allows you to massage the ownerProps for the base component.
// mapProps(
// propsMapper: (ownerProps: Object) => Object,
// ): HigherOrderComponent
const alwaysSteve = mapProps({ name: 'steve' })
The withProps higher order component allows you to merge new props with the ownerProps of the base component.
// withProps(
// createProps: (ownerProps: Object) => Object | Object
// ): HigherOrderComponent
const mergeSteveAndKeepTheRest = withProps({ name: 'Steve' })
The renderComponent higher order component allows you to... well, render a component. It's useful in conjunction with the next HOC.
// renderComponent(
// Component: ReactClass | ReactFunctionalComponent | string
// ): HigherOrderComponent
const User = ({ name }) => <span>{name}</span>
const hocUser = renderComponent(User)
The branch higher order component allows you to conditionally choose between higher order components. Really powerful when used with renderComponent or renderNothing
// branch(
// test: (props: Object) => boolean,
// left: HigherOrderComponent,
// right: ?HigherOrderComponent
// ): HigherOrderComponent
const Left = () => <span>Left</span>
const Right = () => <span>Right</span>
const predicate = ({ isLoading }) => isLoading
const withLoading = branch(
predicate,
renderComponent(Left),
renderComponent(Right) // This can be implicitly supplied by the base component
)
We'll make a higher order component that will show a spinner until our data has loaded that we can use on any similar component.
By Kyle Shevlin
Learn to use and abuse higher order components with the Recompose library.
Front End JavaScript Developer building apps with React and Redux. Also a fan of Node, Rails, D3 and more.