Pro Yearly is on sale from $80 to $50! »

Composition on High-Order Components

Ed35943d3199ea37b1b60c39615e8163?s=47 Raphael Amorim
August 22, 2018
32

Composition on High-Order Components

Ed35943d3199ea37b1b60c39615e8163?s=128

Raphael Amorim

August 22, 2018
Tweet

Transcript

  1. Composition on hIGH-ORDER cOMPONENTS RAPHAEL AMORIM

  2. Hi, I’m Raphael. @raphamorims

  3. godaddy.com/careers

  4. Disclaimer. (You will probably not need a couple of things

    presented here)
  5. "all the cool kids are doing it." So why this

    talk?
  6. The goal of this talk is to present 
 Composition

    on React Components.
  7. The goal of this talk is to present 
 Composition

    on React Components. • Fast introduction to HOC • Composition on real world • Extra: Recompose library
  8. HOC :: Component -> Component

  9. class CommentList extends React.Component { constructor(props) { super(props); this.state =

    { comments: DataSource.getComments() }; } render() { return ( <div> {this.state.comments.map((comment) => ( <Comment comment={comment} key={comment.id} /> ))} </div> ); } }
  10. class BlogPost extends React.Component { constructor(props) { super(props); this.state =

    { blogPost: DataSource.getBlogPost(props.id) }; } render() { return <TextBlock text={this.state.blogPost} />; } }
  11. const CommentListWithSubscription = withSubscription( CommentList, (DataSource) => DataSource.getComments() ); const

    BlogPostWithSubscription = withSubscription( BlogPost, (DataSource, props) => DataSource.getBlogPost(props.id) );
  12. Ok Raphael, we got it.

  13. Where you’re planning to go with it?

  14. Present HOC compositions as an option

  15. github.com/raphamorim/react-motions

  16. import { Shake } from 'react-motions' const ComponentWithShake = ()

    => ( <Shake> <React.Fragment> Shake your booty, shake your booty 
 </React.Fragment> </Shake> )
  17. import { withShake } from 'react-motions' const ComponentWithShake = ()

    => withShake( <React.Fragment> Shake your booty, shake your booty 
 </React.Fragment> )
  18. import { Bounce } from 'react-motions' const DoNotStopBouncing = ()

    => ( <Bounce infinite> <React.Fragment>Bouncing…</React.Fragment> </Bounce> )
  19. import { withBounce, withInfinite } from 'react-motions' const DoNotStopBouncing =

    withInfinite(
 withBounce(
 <React.Fragment>Bouncing…</React.Fragment>
 )
 )
  20. import { 
 withBounce, withShake, withInfinite, withSequence 
 } from

    'react-motions' const Component = <div>How can I look beautiful</div> const ComponentWithShake = withShake(Component) const ComponentWithShakeAndBounce = withShake(withBounce(Component))
 const ComponentWithInfiniteBounce = withInfinite(withBounce(Component))
 const ComponentWithShakeThenBounce = withSequence(withShake(withBounce(Component))) Recap
  21. import { 
 withBounce, withShake, withInfinite, withSequence 
 } from

    'react-motions' const Component = <div>How can I look beautiful</div> const ComponentWithShake = withShake(Component)
 const ComponentWithShakeAndBounce = withShake(withBounce(Component))
 const ComponentWithInfiniteBounce = withInfinite(withBounce(Component))
 const ComponentWithShakeThenBounce = withSequence(withShake(withBounce(Component)))
  22. import { 
 withBounce, withShake, withInfinite, withSequence 
 } from

    'react-motions' const Component = <div>How can I look beautiful</div> const ComponentWithShake = withShake(Component)
 const ComponentWithShakeAndBounce = withShake(withBounce(Component)) 
 const ComponentWithInfiniteBounce = withInfinite(withBounce(Component))
 const ComponentWithShakeThenBounce = withSequence(withShake(withBounce(Component)))
  23. import { 
 withBounce, withShake, withInfinite, withSequence 
 } from

    'react-motions' const Component = <div>How can I look beautiful</div> const ComponentWithShake = withShake(Component)
 const ComponentWithShakeAndBounce = withShake(withBounce(Component)) 
 const ComponentWithInfiniteBounce = withInfinite(withBounce(Component))
 const ComponentWithShakeThenBounce = withSequence(withShake(withBounce(Component)))
  24. const Component = () => ( withInfinite( withFadeOut( withInfinite( withShake(

    <h2>Bouncing and Fading Out infinitely!!</h2> ) ) ) ) )
  25. Extra!

  26. github.com/acdlite/recompose

  27. import { withState, enhance } from ‘recompose'
 
 const enhance

    = withState('counter', 'setCounter', 0) const Counter = enhance(({ counter, setCounter }) => <React.Fragment> Count: {counter} <button onClick={() => setCounter(n => n + 1)}>Increment</button> <button onClick={() => setCounter(n => n - 1)}>Decrement</button> </React.Fragment> )
  28. import { branch, enhance, renderComponent } from ‘recompose'
 
 //

    `isLoading()` is a function that returns whether or not the component // is in a loading state const spinnerWhileLoading = isLoading => branch( isLoading, renderComponent(Spinner) // `Spinner` is a React component ) // Now use the `spinnerWhileLoading()` helper to add a loading spinner to any // base component const enhance = spinnerWhileLoading( props => !(props.title && props.author && props.content) )
 const Post = enhance(({ title, author, content }) => <article> <h1>{title}</h1> <h2>By {author.name}</h2> <div>{content}</div> </article> )
  29. // A component that is expensive to render const ExpensiveComponent

    = ({ propA, propB }) => {...} // Optimized version of same component, using shallow comparison of props // Same effect as extending React.PureComponent const OptimizedComponent = pure(ExpensiveComponent) // Even more optimized: only updates if specific prop keys have changed const HyperOptimizedComponent = onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)
  30. const enhance = compose( // This is a Recompose-friendly version

    of Relay.createContainer(), provided by recompose-relay createContainer({ fragments: { post: () => Relay.QL` fragment on Post { title, content } ` } }), flattenProp('post') ) const Post = enhance(({ title, content }) => <article> <h1>{title}</h1> <div>{content}</div> </article> )
  31. // Any Recompose module can be imported individually import getDisplayName

    from 'recompose/getDisplayName' ConnectedComponent.displayName = `connect(${getDisplayName(BaseComponent)})` // Or, even better: import wrapDisplayName from 'recompose/wrapDisplayName' ConnectedComponent.displayName = wrapDisplayName(BaseComponent, 'connect') import toClass from 'recompose/toClass' // Converts a function component to a class component, e.g. so it can be given // a ref. Returns class components as is. const ClassComponent = toClass(FunctionComponent)
  32. github.com/acdlite/recompose/blob/master/docs/API.md

  33. Thanks!
 Obrigado!