Slide 1

Slide 1 text

React Component Anti-Patterns Radoslav Stankov

Slide 2

Slide 2 text

πŸ‘‹

Slide 3

Slide 3 text

Radoslav Stankov @rstankov

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

https://rstankov.com/appearances

Slide 7

Slide 7 text

Plan for today

Slide 8

Slide 8 text

1. History lesson 2. What is a good React component 3. React anti-patterns Plan for today

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

1 History lesson

Slide 11

Slide 11 text

My history with React

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

React history

Slide 14

Slide 14 text

🍬 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

Slide 15

Slide 15 text

2 What is a good React component?

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

Is the component easy to understand?

Slide 18

Slide 18 text

~/components/[Component]/index.js

Slide 19

Slide 19 text

1) import s 2) type s 3) constant s 4) default export of main componen t 5) subcomponents, hooks, functions

Slide 20

Slide 20 text

// 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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

3 React anti-patterns

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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.

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

1. Nam e 2. Example s 3. What is the proble m 4. Alternatives

Slide 30

Slide 30 text

Overwrite CSS of inner-component

Slide 31

Slide 31 text

function Component({ className }) { return (

something

) ; } Overwrite CSS of inner component β›”

Slide 32

Slide 32 text

Overwrite CSS of inner component β›”

Slide 33

Slide 33 text

.foo h1 { color: red ; } Overwrite CSS of inner component βœ…

Slide 34

Slide 34 text

Pass className to inner component

Slide 35

Slide 35 text

Pass className to inner component β›”

Slide 36

Slide 36 text

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

something

) ; } Pass className to inner component β›”

Slide 37

Slide 37 text

Pass className to inner component βœ…

Slide 38

Slide 38 text

whatever

Pass className to inner component βœ…

Slide 39

Slide 39 text

Overuse of renderless components

Slide 40

Slide 40 text

Overuse of renderless components β›”

Slide 41

Slide 41 text

useOnWindowResize(onResize ) useOnWindowScroll(onScroll) Overuse of renderless components βœ…

Slide 42

Slide 42 text

Class components with render methods

Slide 43

Slide 43 text

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 β›”

    Slide 44

    Slide 44 text

    function Component({ items }) { return (
      {items.map((item) => ( ))}
    ) ; } function Item({ item }) { return
  • {item.title}
  • ; } Class components with render methods βœ…

    Slide 45

    Slide 45 text

    Constant map instead of components

    Slide 46

    Slide 46 text

    const LINKS = [ { to: '/', title: 'Home' } , { to: '/about', title: 'About' } , { to: '/contact', title: 'Contact' } , // ... ] ; LIKES.map((link, i) => ( )); Constant map instead of components β›”

    Slide 47

    Slide 47 text

    Constant map instead of components βœ…

    Slide 48

    Slide 48 text

    Decompose record to props

    Slide 49

    Slide 49 text

    Decompose record to props β›”

    Slide 50

    Slide 50 text

    Decompose record to props β›”

    Slide 51

    Slide 51 text

    Decompose record to props βœ…

    Slide 52

    Slide 52 text

    
 http://graphql.org/ 


    Slide 53

    Slide 53 text

    No content

    Slide 54

    Slide 54 text

    apollo client:codegen

    Slide 55

    Slide 55 text

    
 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 } `;

    Slide 56

    Slide 56 text

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


    Slide 57

    Slide 57 text

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

    Slide 58

    Slide 58 text

    
 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 } `;

    Slide 59

    Slide 59 text

    
 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 } `;

    Slide 60

    Slide 60 text

    Query Fragment Fragment Fragment Fragment Fragment Fragment

    Slide 61

    Slide 61 text

    Screen Component Component Component Component Component Component

    Slide 62

    Slide 62 text

    Decompose record to props βœ…

    Slide 63

    Slide 63 text

    Wildcard typing

    Slide 64

    Slide 64 text

    Wildcard typing interface IProps { post: any; onClick: any; color: string; [key: string]: value; } β›”

    Slide 65

    Slide 65 text

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

    Slide 66

    Slide 66 text

    Component as property

    Slide 67

    Slide 67 text

    
 Component as property β›”

    Slide 68

    Slide 68 text

    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

    Slide 69

    Slide 69 text

    interface IProps { component: 'h1' | 'h2' | 'h3' | 'p'; } Component as property βœ…

    Slide 70

    Slide 70 text

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

    Slide 71

    Slide 71 text

    Unexpected defaults

    Slide 72

    Slide 72 text

    Unexpected defaults β›”

    Slide 73

    Slide 73 text

    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 βœ…

    Slide 74

    Slide 74 text

    Using two components instead of one

    Slide 75

    Slide 75 text

    <> {isActive && } {!isActive && } > Using two components instead of one β›”

    Slide 76

    Slide 76 text

    {isActive ? : } Using two components instead of one βœ…

    Slide 77

    Slide 77 text

    <>
 Only on phon e On other device s > Using two components instead of one β›”

    Slide 78

    Slide 78 text

    {(isPhone) => ( isPhone ? 'Only on phone' : 'On other devices ' )} Using two components instead of one βœ…

    Slide 79

    Slide 79 text

    Unneeded closures

    Slide 80

    Slide 80 text

    function Component({ clickHandle }) { return ( clickHandle(e)} > clic k ) ; } Unneeded closures β›”

    Slide 81

    Slide 81 text

    function Component({ clickHandle }) { return ( clic k ) ; } Unneeded closures βœ…

    Slide 82

    Slide 82 text

    Unneeded loops

    Slide 83

    Slide 83 text

    extractEdgeNode(connection).map((n) => ( )); Unneeded loops β›”

    Slide 84

    Slide 84 text

    mapEdgeNodes(connection, (n) => ( ) ); Unneeded loops βœ…

    Slide 85

    Slide 85 text

    const nodes = extractEdgeNode(connection) ; if (!nodes.length) { return null ; } return nodes.map((n) => ( ) ); Unneeded loops β›”

    Slide 86

    Slide 86 text

    if (areEdgeNodesEmpty(connection)) { return null ; } return mapEdgeNodes(connection, (n) => ( ) ); Unneeded loops βœ…

    Slide 87

    Slide 87 text

    const nodes = extractEdgeNode(connection)[0]; Unneeded loops β›”

    Slide 88

    Slide 88 text

    const nodes = first(connection); Unneeded loops βœ…

    Slide 89

    Slide 89 text

    Recap

    Slide 90

    Slide 90 text

    πŸš— 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

    Slide 91

    Slide 91 text

    No content

    Slide 92

    Slide 92 text

    No content

    Slide 93

    Slide 93 text

    Thanks 😎

    Slide 94

    Slide 94 text

    https://speakerdeck.com/rstankov