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. React Component
    Anti-Patterns
    Radoslav Stankov


    View Slide

  2. 👋

    View Slide

  3. Radoslav Stankov
    @rstankov

    View Slide

  4. View Slide

  5. View Slide

  6. https://rstankov.com/appearances

    View Slide

  7. Plan for today

    View Slide

  8. 1. History lesson


    2. What is a good React component


    3. React anti-patterns
    Plan for today

    View Slide

  9. View Slide

  10. 1
    History lesson

    View Slide

  11. My history with React

    View Slide

  12. October 2014 Backbone
    early 2014 jQuery spaghetti
    February 2014 React on Rails
    May 2014 custom Flux
    December 2015 Redux

    View Slide

  13. React history

    View Slide

  14. 🍬 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

    View Slide

  15. 2
    What is a good React component?

    View Slide

  16. 🍬 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?

    View Slide

  17. Is the component easy to understand?

    View Slide

  18. ~/components/[Component]/index.js

    View Slide

  19. 1) import
    s

    2) type
    s

    3) constant
    s

    4) default export of main componen
    t

    5) subcomponents, hooks, functions

    View Slide

  20. // 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
    ;

    }
    // 3.3) The happy path of the component
    return My component
    ;

    }
    // 4) Everything else, like helper functions, types, custom hooks, sub components

    View Slide

  21. ~/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

    View Slide

  22. 3
    React anti-patterns

    View Slide

  23. What is Anti-Pattern?
    Anti-patterns are certain patterns in software
    development that are considered bad
    programming practices.

    View Slide

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

    View Slide

  25. View Slide

  26. View Slide

  27. View Slide

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

    View Slide

  29. 1. Nam
    e

    2. Example
    s

    3. What is the proble
    m

    4. Alternatives

    View Slide

  30. Overwrite CSS of inner-component

    View Slide

  31. function Component({ className }) {
    return
    (


    something

    )
    ;

    }
    Overwrite CSS of inner component

    View Slide


  32. Overwrite CSS of inner component

    View Slide

  33. .foo h1 {
    color: red
    ;

    }
    Overwrite CSS of inner component

    View Slide

  34. Pass className to inner component

    View Slide



  35. className={styles.foo}


    headerClassName={styles.bar} />
    Pass className to inner component

    View Slide

  36. function Component({ className, headerClassName }) {
    return
    (


    something

    )
    ;

    }
    Pass className to inner component

    View Slide


  37. Pass className to inner component

    View Slide

  38. >

    whatever

    Pass className to inner component

    View Slide

  39. Overuse of renderless components

    View Slide



  40. Overuse of renderless components

    View Slide

  41. useOnWindowResize(onResize
    )

    useOnWindowScroll(onScroll)
    Overuse of renderless components

    View Slide

  42. Class components with render methods

    View Slide

  43. class Component extends React.Component {
    render() {
    return {this.renderItems()}
    ;

    }
    renderItems() {
    return this.props.items.map(this.renderItem)
    ;

    }
    renderItem(item) {
    return {item.title}
    ;

    }
    }
    Class components with render methods

    View Slide

  44. function Component({ items }) {
    return
    (


    {items.map((item) =>
    (


    ))}

    )
    ;

    }
    function Item({ item }) {
    return {item.title}
    ;

    }
    Class components with render methods

    View Slide

  45. Constant map instead of components

    View Slide

  46. const LINKS = [
    { to: '/', title: 'Home' }
    ,

    { to: '/about', title: 'About' }
    ,

    { to: '/contact', title: 'Contact' }
    ,

    // ...
    ]
    ;

    LIKES.map((link, i) =>
    (

    >

    ));
    Constant map instead of components

    View Slide




  47. Constant map instead of components

    View Slide

  48. Decompose record to props

    View Slide


  49. Decompose record to props

    View Slide

  50. postId={post.id}
    postTitle={post.title}
    postVoteCount={post.votesCount}
    />
    Decompose record to props

    View Slide


  51. Decompose record to props

    View Slide


  52. http://graphql.org/

    View Slide

  53. View Slide

  54. apollo client:codegen

    View Slide


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

    }

    `;

    View Slide

  56. // ===================================================
    =

    // GraphQL fragment: ProfileAvatarFragmen
    t

    // ===================================================
    =

    export interface ProfileAvatarFragment
    {

    __typename: "Profile"
    ;

    id: string
    ;

    name: string
    ;

    kind: string
    ;

    imageUrl: string | null
    ;

    }

    graphql/types.ts

    View Slide

  57. import { ProfileAvatarFragment } from '~/types/graphql';

    View Slide


  58. 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
    }

    `;

    View Slide


  59. 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
    }

    `;

    View Slide

  60. Query
    Fragment Fragment
    Fragment
    Fragment
    Fragment
    Fragment

    View Slide

  61. Screen
    Component Component
    Component
    Component
    Component
    Component

    View Slide


  62. Decompose record to props

    View Slide

  63. Wildcard typing

    View Slide

  64. Wildcard typing
    interface IProps {
    post: any;
    onClick: any;
    color: string;
    [key: string]: value;
    }

    View Slide

  65. Wildcard typing
    interface IProps extends React.HTMLAttributes {
    post: IPost;
    onClick: () => void;
    color: 'blue' | 'red';
    }

    View Slide

  66. Component as property

    View Slide




  67. Component as property

    View Slide

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

    View Slide

  69. interface IProps {
    component: 'h1' | 'h2' | 'h3' | 'p';
    }
    Component as property

    View Slide

  70. interface IProps extends React.HTMLAttributes {
    component: 'h1' | 'h2' | 'h3' | 'p';
    }
    Component as property

    View Slide

  71. Unexpected defaults

    View Slide


  72. Unexpected defaults

    View Slide

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

    View Slide

  74. Using two components instead of one

    View Slide

  75. <>
    {isActive && }
    {!isActive && }
    >
    Using two components instead of one

    View Slide

  76. {isActive ? : }
    Using two components instead of one

    View Slide

  77. <>


    Only on phon
    e



    On other device
    s


    >
    Using two components instead of one

    View Slide


  78. {(isPhone) =>
    (

    isPhone ? 'Only on phone' : 'On other devices
    '

    )}

    Using two components instead of one

    View Slide

  79. Unneeded closures

    View Slide

  80. function Component({ clickHandle }) {
    return
    (

    clickHandle(e)}
    >

    clic
    k

    >

    )
    ;

    }
    Unneeded closures

    View Slide

  81. function Component({ clickHandle }) {
    return
    (

    >

    clic
    k

    >

    )
    ;

    }
    Unneeded closures

    View Slide

  82. Unneeded loops

    View Slide

  83. extractEdgeNode(connection).map((n) =>
    (

    >

    ));
    Unneeded loops

    View Slide

  84. mapEdgeNodes(connection, (n) =>
    (


    )

    );
    Unneeded loops

    View Slide

  85. const nodes = extractEdgeNode(connection)
    ;

    if (!nodes.length) {
    return null
    ;

    }
    return nodes.map((n) =>
    (


    )

    );
    Unneeded loops

    View Slide

  86. if (areEdgeNodesEmpty(connection)) {
    return null
    ;

    }
    return mapEdgeNodes(connection, (n) =>
    (


    )

    );
    Unneeded loops

    View Slide

  87. const nodes = extractEdgeNode(connection)[0];
    Unneeded loops

    View Slide

  88. const nodes = first(connection);
    Unneeded loops

    View Slide

  89. Recap

    View Slide

  90. 🚗 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

    View Slide

  91. View Slide

  92. View Slide

  93. Thanks 😎

    View Slide

  94. https://speakerdeck.com/rstankov

    View Slide