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.