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

Simplify Your React Component With Recompose

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for wuct 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.

Avatar for wuct

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} !/> )