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

React Component Anti-Patterns

React Component Anti-Patterns

Radoslav Stankov

June 03, 2021
Tweet

More Decks by Radoslav Stankov

Other Decks in Technology

Transcript

  1. 1. History lesson 2. What is a good React component

    3. React anti-patterns Plan for today
  2. October 2014 Backbone early 2014 jQuery spaghetti February 2014 React

    on Rails May 2014 custom Flux December 2015 Redux
  3. 🍬 class component s 🍫 css module s 🍰 style

    component s 🍪 functional component s 🍩 high-order components
 🍥 redu x 🍨 renderless component s 🍧 children as functio n 🍦 GraphQ L 🎂 hooks
  4. 🍬 What are the props this component needs ? 🍫

    What does this component do ? 🍪 What are the component's states ? 🍩 What is the component's logic ? 🍨 How can we trace the logic ? 🍧 Is it easy to refactor?
  5. 1) import s 2) type s 3) constant s 4)

    default export of main componen t 5) subcomponents, hooks, functions
  6. // 1) imports import * as React from 'react' ;

    // ... // 2) component props (always named IProps) interface IProps { // ... } // 2) (optional) constants const SOME_VALUE = 1 ; // ... // 3) default export of main component export default function ComponentName({ prop1, prop2 = 'default value', }: IProps) { // 3.1) Hook s // 3.2) Guard clauses if (someCondition) { return null ; } if (someOtherCondiation) { return <Placeholder /> ; } // 3.3) The happy path of the component return <div>My component</div> ; } // 4) Everything else, like helper functions, types, custom hooks, sub components
  7. ~/components/[Component]/index.ts x ~/components/[Component]/Fragment.graphq l ~/components/[Component]/Mutation.graphq l ~/components/[Component]/styles.module.cs s ~/components/[Component]/[icon].sv g

    ~/components/[Component]/utils.t s ~/components/[Component]/utils.test.t s ~/components/[Component]/[PrivateSubComponent]/index.ts x ~/components/[Component]/[PrivateSubComponent]/... Component as folder
  8. What is Anti-Pattern? As opposed to design patterns which are

    common approaches to common problems which have been formalized and are generally considered a good development practice, anti-patterns are the opposite and are undesirable.
  9. Reasons for anti-patterns • React and ecosystem evolved • JS

    -> Flow -> TS • We have inconsistent UI in designs • Workarounds • We learned among the way • Quick fi xes and path of least resistance
  10. 1. Nam e 2. Example s 3. What is the

    proble m 4. Alternatives
  11. function Component({ className, headerClassName }) { return ( <div className={className}>

    <h1 className={headerClassName}>something</h1> </div> ) ; } Pass className to inner component ⛔
  12. class Component extends React.Component { render() { return <ul>{this.renderItems()}</ul> ;

    } renderItems() { return this.props.items.map(this.renderItem) ; } renderItem(item) { return <li>{item.title}</li> ; } } Class components with render methods ⛔
  13. function Component({ items }) { return ( <ul> {items.map((item) =>

    ( <Item key={item.id} item={item} /> ))} </ul> ) ; } function Item({ item }) { return <li>{item.title}</li> ; } Class components with render methods ✅
  14. const LINKS = [ { to: '/', title: 'Home' }

    , { to: '/about', title: 'About' } , { to: '/contact', title: 'Contact' } , // ... ] ; LIKES.map((link, i) => ( <MenuLink key={i} to={link.to} title={link.title} / > )); Constant map instead of components ⛔
  15. 
 components/Pro fi leAvatar/Fragment.ts 
 import gql from 'graphql-tag' ;

    export default gql` fragment ProfileAvatarFragment on Profile { i d nam e kin d imageUr l } `;
  16. // =================================================== = // GraphQL fragment: ProfileAvatarFragmen t // ===================================================

    = export interface ProfileAvatarFragment { __typename: "Profile" ; id: string ; name: string ; kind: string ; imageUrl: string | null ; } 
 graphql/types.ts 

  17. 
 components/Pro fi leCard/Fragment.ts 
 import gql from 'graphql-tag' ;

    import ProfileAvatarFragment from '~/components/ProfileAvatar/Fragment' ; export default gql` fragment ProfileCardFragment on Profile { i d nam e slu g ...ProfileAvatarFragmen t } ${ProfileAvatarFragment } `;
  18. 
 screens/Pro fi le/Query.ts 
 import gql from 'graphql-tag' ;

    import ProfileCardFragment from '~/components/ProfileCard/Fragment';
 // ... more fragment s export default gql` query ProfileScreen($id: ID!) { profile(id: $id) { i d ...ProfileCardFragmen t // ... more fragment s } } ${ProfileCardFragment } `;
  19. <Text component="p" {...props} /> <Text component={Link} {...props} /> 
 <Text

    component={Flex} {...props} /> Component as property ⛔
  20. interface IProps { // ... /** Props passed down to

    the custom `component` */ dangerouslySetInnerHTML?: { __html: string | null }; href?: string; id?: string; itemMargin?: string; justify?: string; meta?: any; onClick?: any; responsive?: false; subjectId?: string; subjectType?: string; target?: string; title?: string | null; to?: string; rel?: string; style?: any; width?: string; colSpan?: number; Component as property
  21. interface IProps { component: 'h1' | 'h2' | 'h3' |

    'p'; } Component as property ✅
  22. 1. Be careful when pick defaults 2. Default is false/inactive

    by default 3. Have couple of cases when default is turn off 4. When turn off default, check if most uses are with it 5. Don't be afraid to change a default 6. Don't be afraid to add default later, when see uses Unexpected defaults ✅
  23. <> {isActive && <Active />} {!isActive && <NotActive />} </>

    Using two components instead of one ⛔
  24. <>
 <Device.Phone> Only on phon e </Device.Phone> <Device.TableOrDesktop> On other

    device s </Device.TableOrDesktop> </> Using two components instead of one ⛔
  25. <Device.Phone> {(isPhone) => ( isPhone ? 'Only on phone' :

    'On other devices ' )} </Device.Phone> Using two components instead of one ✅
  26. function Component({ clickHandle }) { return ( <button onClick={(e) =>

    clickHandle(e)} > clic k </button > ) ; } Unneeded closures ⛔
  27. const nodes = extractEdgeNode(connection) ; if (!nodes.length) { return null

    ; } return nodes.map((n) => ( <Node node={node} key={node.id} /> ) ); Unneeded loops ⛔
  28. if (areEdgeNodesEmpty(connection)) { return null ; } return mapEdgeNodes(connection, (n)

    => ( <Node node={node} key={node.id} /> ) ); Unneeded loops ✅
  29. 🚗 Overwrite CSS of inner-component 🚙 Pass className to inner

    component 🏎 Overuse of renderless components 🚕 Class components with render methods 🚓 Constant map instead of components 🚙 Decompose record to props 🚚 Wildcard typing 🚐 Component as property 🚎 Unexpected defaults 🚐 Using two components instead of one 
 🚛 Unneeded closures 🚌 Unneeded loops 🛻 Variant property