Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Simplify Your React Component With Recompose

wuct
September 21, 2017

Simplify Your React Component With Recompose

This slide is from the Recompose talk which I gave in a Front-end Developers Taiwan meetup.

wuct

September 21, 2017
Tweet

More Decks by wuct

Other Decks in Programming

Transcript

  1. About Recompose • Lodash for React. • Can be used

    with React, Preact and all other React-like framework. • Flow typed. • 繁體中⽂文⽂文件(thanks to @neighborhood999) • I’m one of the collaborators :D
  2. Why We Should use Recompose? • To write more pure

    functional components. • Functional programming is fun!
  3. Pure Functional Component • Help prevent abuse of using setSate().

    • Make code more readable. • Make testing easier.
  4. Basic Functional Programming: Currying • From Wikipedia:
 Currying is the

    technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument. Currying is related to, but not the same as, partial application. const add = x !=> y !=> x + y add(4)(2) !// 6 addFour = add(4) addFour(2) !// 6
  5. Curry in JavaScript • Because of JavaScript’s syntax, the implementation

    of curry in JS is usually slightly different from the origin definition. import R from 'ramda' !// Lodash implementation is equivalent. const addThreeNumbers = (x, y, z) !=> x + y + z const curriedAddThreeNumbers = R.curry(addThreeNumbers) curriedAddThreeNumbers(1, 2, 3) curriedAddThreeNumbers(1, 2)(3) curriedAddThreeNumbers(1)(2, 3) curriedAddThreeNumbers(1)(2)(3)
  6. Basic Functional Programming: Function Composition • From Wikipedia:
 Function composition

    is an act or mechanism to combine simple functions to build more complicated ones. … the result of each function is passed as the argument of the next, and the result of the last one is the result of the whole. • In math:
 f(x) = 2x+3
 g(x) = x^2
 h = (g º f)(x)
 = g(f(x))
 = (2x + 3) ^ 2 const f = x !=> 2 * x + 3 const g = x !=> x ^ 2 const h = compose(g, f) h(2) !// 49
  7. How to Implement Compose in JavaScript const compose = (!!...funcs)

    !=> { if (funcs.length !!=== 0) { return arg !=> arg } if (funcs.length !!=== 1) { return funcs[0] } return funcs.reduce((a, b) !=> (!!...args) !=> a(b(!!...args))) }
  8. Basic Functional Programming: Higher-order Function • From Wikipedia:
 A function

    that does at least one of the following:
 1. Takes one or more functions as arguments.
 2. Returns a function as its result. • Example: curry(), compose()
  9. Now We Get All The Tools to Have Fun !//

    map() and filter() are HOFs const map = fun !=> array !=> array.map(fun) const filter = fun !=> array !=> array.filter(fun) const increaseAll = map(x !=> x + 1) const takeEven = filter(x !=> x % 2 !!=== 0) const increaseAllAndTakeEven = compose( takeEven, increaseAll ) increaseAllAndTakeEven([1, 2, 3, 4, 5]) !// [2, 4, 6]
  10. Higher-order Component • A function that does at least one

    of the following:
 1. Takes one or more components as arguments.
 2. Returns a component as its result. • Remember that, in React, functions can also be components (a.k.a. pure functional component). • Example: • react-redux’ connect() • styled-components’ withTheme() • react-router’s withRouter() • react-apollo’s graphql() • Most of functions in Recompose
  11. import * as Rc from 'recompose' !// enhance is a

    HOC, which is a function accepting !// a component and return a new component with the !// “MyAwesomeButton" display name. const enhance = Rc.setDisplayName('MyAwesomeButton') const MyAwesomeButton = (!!...props) !=> <button {!!...props}>This is awesome!!</button> export default enhance(MyAwesomeButton) setDisplayName( displayName: string ): HigherOrderComponent
  12. Some Commonly Used HOCs • pure • mapProps • withState

    • withHandlers • withStateHandlers
  13. const enhance = Rc.pure const MyComponent = () !=> <div>{!/*

    !!... !*/}!</div> export default enhance(MyComponent) pure() pure: HigherOrderComponent
  14. const enhance = Rc.mapProps(({ firstName, secondName }) !=> ({ name:

    firstName + ' ' + secondName })) const Name = ({ name }) !=> <span>{name}!</span> export default enhance(Name) mapProps( propsMapper: (ownerProps: Object) => Object, ): HigherOrderComponent
  15. const enhance = Rc.withState('count', 'setCount', 0) const MyComponent = ({

    count, setCount }) !=> <div onClick={() !=> setCount(count + 1)}> Click {count} {count > 1 ? 'times' : 'time'}. !</div> export default enhance(MyComponent) withState( stateName: string, stateUpdaterName: string, initialState: any | (props: Object) => any ): HigherOrderComponent
  16. const enhance = Rc.compose( Rc.withState('count', 'setCount', 0), Rc.withHandlers({ onClick: ({

    count, setCount }) !=> () !=> setCount(count + 1) }) ) const MyComponent = ({ count, onClick }) !=> <div onClick={onClick}> Click {count} {count > 1 ? 'times' : 'time'}. !</div> export default enhance(MyComponent) withHandlers( handlerCreators: { [handlerName: string]: (props: Object) => Function } | handlerCreatorsFactory: (initialProps) => { [handlerName: string]: (props: Object) => Function } ): HigherOrderComponent
  17. const enhance = Rc.withStateHandlers({ count: 0 }, { onClick: ({

    count }) !=> () !=> ({ count: count + 1 }) }) const MyComponent = ({ count, onClick }) !=> <div onClick={onClick}> Click {count} {count > 1 ? 'times' : 'time'}. !</div> export default enhance(MyComponent) withStateHandlers( initialState: Object | (props: Object) => any, stateUpdaters: { [key: string]: (state:Object, props:Object) => (...payload: any[]) => Object } )
  18. Usage With Redux export default Rc.compose( connect( ({ address })

    !=> ({ address }), { submitAddress }, ), Rc.withStateHandlers( ({ address }) !=> ({ address }), { onChange: () !=> e !=> ({ address: e.target.value }), onSubmit: ({ address }, { submitAddress }) !=> e !=> { e.preventDefault() submitAddress(address) } } ), Rc.pure )(({ address, onChange, onSubmit }) !=> <form onSubmit={onSubmit}> <input value={address} onChange={onChange} !/> <button>Submit!</button> !</form> )
  19. Usage With Apollo export default Rc.compose( Rc.withStateHandlers({ keyword: '' },

    { onChange: () !=> e !=> ({ keyword: e.target.value }), }), graphql(gql` query HotelSearchQuery($keyword: String!) { hotels(keyword: $keyword) { !!... } }`, { options: ({ keyword }) !=> ({ variables: { keyword }}), }), mapProps(({ data, !!...otherProps }) !=> ({ !!...otherProps, !!...data, })), Rc.pure, )(({ keyword, onChange, hotels }) !=> <div> <input value={keyword} onChange={onChange} !/> <ul>{hotels.map(!/* !!... !*/)}!</ul> !</div> )
  20. Usage With React Router export default Rc.compose( withRouter, Rc.mapProps(({ match:

    { params = {} }}) !=> ({ keyword: params.keyword, })), graphql(gql` !!... `, { options: ({ keyword }) !=> ({ variables: { keyword }}), }), Rc.pure )(({ hotels }) !=> <div> <ul>{hotels.map(!/* !!... !*/)}!</ul> !</div> )
  21. Usage With Observable mapPropsStream( ownerPropsToChildProps: (props$: Observable<object>) => Observable<object>, BaseComponent:

    ReactElementType ): ReactComponent Rc.mapPropsStream(props$ !=> { const value$ = new BehaviorSubject('') const onClick = e !=> value$.next(e.target.value) return props$.combineLatest( value$.distinctUntilChanged(), (props, value) !=> ({ !!...props, value, onChange }) ) })(({ value, onChange }) !=> <input value={value} onChange={onChange} !/> )