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

Composition on High-Order Components

Raphael Amorim
August 22, 2018
83

Composition on High-Order Components

Raphael Amorim

August 22, 2018
Tweet

Transcript

  1. The goal of this talk is to present 
 Composition

    on React Components. • Fast introduction to HOC • Composition on real world • Extra: Recompose library
  2. 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> ); } }
  3. class BlogPost extends React.Component { constructor(props) { super(props); this.state =

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

    BlogPostWithSubscription = withSubscription( BlogPost, (DataSource, props) => DataSource.getBlogPost(props.id) );
  5. import { Shake } from 'react-motions' const ComponentWithShake = ()

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

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

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

    withInfinite(
 withBounce(
 <React.Fragment>Bouncing…</React.Fragment>
 )
 )
  9. 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
  10. 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)))
  11. 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)))
  12. 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)))
  13. const Component = () => ( withInfinite( withFadeOut( withInfinite( withShake(

    <h2>Bouncing and Fading Out infinitely!!</h2> ) ) ) ) )
  14. 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> )
  15. 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> )
  16. // 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)
  17. 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> )
  18. // 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)