$30 off During Our Annual Pro Sale. View Details »

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. Simplify Your React
    Component With Recompose
    wu_ct
    wuct

    View Slide

  2. Sample Code is Here
    https://github.com/wuct/
    recompose-f2etw

    View Slide

  3. 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

    View Slide

  4. Why We Should use
    Recompose?
    • To write more pure functional components.

    • Functional programming is fun!

    View Slide

  5. Pure Functional Component
    • Help prevent abuse of using setSate().

    • Make code more readable.

    • Make testing easier.

    View Slide

  6. 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

    View Slide

  7. 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)

    View Slide

  8. 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

    View Slide

  9. 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)))
    }

    View Slide

  10. 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()

    View Slide

  11. 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]

    View Slide

  12. 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

    View Slide

  13. 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) !=>
    This is awesome!!
    export default enhance(MyAwesomeButton)
    setDisplayName(
    displayName: string
    ): HigherOrderComponent

    View Slide

  14. Some Commonly Used
    HOCs
    • pure

    • mapProps

    • withState

    • withHandlers

    • withStateHandlers

    View Slide

  15. const enhance = Rc.pure
    const MyComponent = () !=> {!/* !!... !*/}!
    export default enhance(MyComponent)
    pure()
    pure: HigherOrderComponent

    View Slide

  16. const enhance =
    Rc.mapProps(({ firstName, secondName }) !=> ({
    name: firstName + ' ' + secondName
    }))
    const Name = ({ name }) !=> {name}!
    export default enhance(Name)
    mapProps(
    propsMapper: (ownerProps: Object) => Object,
    ): HigherOrderComponent

    View Slide

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

    View Slide

  18. const enhance = Rc.compose(
    Rc.withState('count', 'setCount', 0),
    Rc.withHandlers({
    onClick: ({ count, setCount }) !=> () !=> setCount(count + 1)
    })
    )
    const MyComponent = ({ count, onClick }) !=>

    Click {count} {count > 1 ? 'times' : 'time'}.
    !
    export default enhance(MyComponent)
    withHandlers(
    handlerCreators: {
    [handlerName: string]: (props: Object) => Function
    } |
    handlerCreatorsFactory: (initialProps) => {
    [handlerName: string]: (props: Object) => Function
    }
    ): HigherOrderComponent

    View Slide

  19. const enhance =
    Rc.withStateHandlers({
    count: 0
    }, {
    onClick: ({ count }) !=> () !=> ({ count: count + 1 })
    })
    const MyComponent = ({ count, onClick }) !=>

    Click {count} {count > 1 ? 'times' : 'time'}.
    !
    export default enhance(MyComponent)
    withStateHandlers(
    initialState: Object | (props: Object) => any,
    stateUpdaters: {
    [key: string]: (state:Object, props:Object) => (...payload: any[]) => Object
    }
    )

    View Slide

  20. 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 }) !=>


    Submit!
    !
    )

    View Slide

  21. 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 }) !=>


    {hotels.map(!/* !!... !*/)}!
    !
    )

    View Slide

  22. 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 }) !=>

    {hotels.map(!/* !!... !*/)}!
    !
    )

    View Slide

  23. Usage With Observable
    mapPropsStream(
    ownerPropsToChildProps: (props$: Observable) => Observable,
    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 }) !=>

    )

    View Slide

  24. Question?
    • What is createEagerElement?

    • Why not add XXX to Recompose?

    View Slide