Advanced Component Patterns

Advanced Component Patterns

React on it’s own has a very small API surface. Most of the magic happens in user-land. It feels like every month, someone from the community finds an innovative way of writing components. This is the best part about React, the creative community.

I’m going to be honest, this stresses me out. I have a constant fear of missing out on the cleanest/prettiest/smartest way of writing React components.

So, I sat and learnt all of them, so that you don’t have to.

Ea26ebbe66c3bb6afb5f711cfe766dff?s=128

Siddharth Kshetrapal

January 19, 2018
Tweet

Transcript

  1. Advanced Component Patterns

  2. siddharthkp

  3. None
  4. I teach React

  5. MUST LEARN ALL PATTERNS

  6. None
  7. 7 component patterns

  8. None
  9. DESIGN DEVELOPMENT

  10. None
  11. None
  12. None
  13. None
  14. const TextInput = props => { return ( <input className="input"

    type="text" placeholder={props.placeholder} /> ) } render( <TextInput placeholder="Enter some text" /> )
  15. None
  16. const TextInput = props => { return ( <input className="input"

    type="text" placeholder={props.placeholder} /> ) } render( <TextInput placeholder="Enter some text" /> )
  17. const TextInput = props => { return ( <input className="input"

    type="text" placeholder={props.placeholder} /> ) } render( <TextInput readOnly placeholder="Enter some text" /> )
  18. const TextInput = props => { let classes = 'input'

    if (props.readOnly) classes += ' readonly' return ( <input className={classes} type="text" placeholder={props.placeholder} /> ) } render( <TextInput readOnly placeholder="Enter some text" /> )
  19. const TextInput = props => { let classes = 'input'

    if (props.readOnly) classes += ' readonly' return ( <input className={classes} type="text" placeholder={props.placeholder} readOnly={props.readOnly} /> ) } render( <TextInput readOnly placeholder="Enter some text" /> )
  20. Functional component

  21. const TextInput = props => { let classes = 'input'

    if (props.readOnly) classes += ' readonly' return ( <input className={classes} type="text" placeholder={props.placeholder} readOnly={props.readOnly} /> ) } render( <TextInput readOnly placeholder="Enter some text" /> )
  22. None
  23. 0 chars

  24. const TextInput = props => { return ( <input className="input"

    type="text" placeholder={props.placeholder} /> ) } 0 chars
  25. const TextInput = props => { return ( <div> <span>0

    chars</span> <input className="input" type="text" placeholder={props.placeholder} /> </div> ) } 0 chars
  26. class TextInput extends React.Component { render() { return ( <div>

    <span>0 chars</span> <input className="input" type="text" placeholder={props.placeholder} /> </div> ) } } 0 chars
  27. class TextInput extends React.Component { render() { return ( <div>

    <span>0 chars</span> <input className="input" type="text" placeholder={this.props.placeholder} /> </div> ) } } 0 chars
  28. class TextInput extends React.Component { constructor(props) { super(props) this.state =

    { length: 0 } } render() { return ( <div> <span>0 chars</span> <input className="input" type="text" placeholder={this.props.placeholder} /> </div> ) } 0 chars
  29. class TextInput extends React.Component { constructor(props) { super(props) this.state =

    { length: 0 } } render() { return ( <div> <span>{this.state.length} chars</span> <input className="input" type="text" placeholder={this.props.placeholder} /> </div> ) } 0 chars
  30. class TextInput extends React.Component { constructor(props) { ... } render()

    { return ( <div> <span>{this.state.length} chars</span> <input className="input" type="text" placeholder={this.props.placeholder} /> </div> ) } 0 chars
  31. class TextInput extends React.Component { constructor(props) { ... } onChange(event)

    { this.setState({ length: event.target.value.length }) } render() { return ( <div> <span>{this.state.length} chars</span> <input className="input" type="text" placeholder={this.props.placeholder} /> </div> ) } 0 chars
  32. class TextInput extends React.Component { constructor(props) { ... } onChange(event)

    { this.setState({ length: event.target.value.length }) } render() { return ( <div> <span>{this.state.length} chars</span> <input className="input" type="text" placeholder={this.props.placeholder onChange={this.onChange.bind(this)} /> </div> ) 0 chars Abc 3 chars
  33. Class component

  34. class TextInput extends React.Component { constructor(props) { ... } onChange(event)

    { this.setState({ length: event.target.value.length }) } render() { return ( <div> <span>{this.state.length} chars</span> <input className="input" type="text" placeholder={this.props.placeholder onChange={this.onChange.bind(this)} /> </div> ) 0 chars Abc 3 chars
  35. class TextInput extends React.Component { constructor(props) { ... } onChange(event)

    { this.setState({ length: event.target.value.length }) } render() { return ( <div> <span>{this.state.length} chars</span> <input className="input" type="text" placeholder={this.props.placeholder onChange={this.onChange.bind(this)} /> </div> ) 0 chars Abc 3 chars
  36. class TextInput extends React.Component { constructor(props) { ... } onChange(event)

    { this.setState({ length: event.target.value.length }) } render() { return ( <div> <span>{this.state.length} chars</span> <input className="input" type="text" placeholder={this.props.placeholder onChange={this.onChange} /> </div> ) 0 chars Abc 3 chars
  37. class TextInput extends React.Component { constructor(props) { ... } onChange

    = (event) => { this.setState({ length: event.target.value.length }) } render() { return ( <div> <span>{this.state.length} chars</span> <input className="input" type="text" placeholder={this.props.placeholder onChange={this.onChange} /> </div> ) 0 chars Abc 3 chars
  38. class TextInput extends React.Component { constructor(props) { ... } onChange

    = (event) => { ... } render() { return ( <div> <span>{this.state.length} chars</span> <input className="input" type="text" placeholder={this.props.placeholder onChange={this.onChange} /> </div> 0 chars Abc 3 chars
  39. class TextInput extends React.Component { constructor(props) { super(props) this.state =

    { length: 0 } } onChange = (event) => { ... } render() { return ( <div> <span>{this.state.length} chars</span> <input className="input" type="text" placeholder={this.props.placeholder onChange={this.onChange} /> </div> 0 chars Abc 3 chars
  40. class TextInput extends React.Component { state = { length: 0

    } onChange = (event) => { ... } render() { return ( <div> <span>{this.state.length} chars</span> <input className="input" type="text" placeholder={this.props.placeholder onChange={this.onChange} /> </div> 0 chars Abc 3 chars
  41. class TextInput extends React.Component { state = { length: 0

    } onChange = (event) => { ... } render() { return ( <div> <span>{this.state.length} chars</span> <input className="input" type="text" placeholder={this.props.placeholder onChange={this.onChange} /> </div> ) } } 0 chars Abc 3 chars
  42. 1/3

  43. class TextInput extends React.Component { state = { length: 0

    } onChange = (event) => { ... } render() { return ( <div> <span>{this.state.length} chars</span> <input className="input" type="text" placeholder={this.props.placeholder onChange={this.onChange} /> </div> ) } } 0 chars Abc 3 chars
  44. 0 chars email Siddharth@Gmail.Com siddharth@gmail.com

  45. class TextInput extends React.Component { render() { return ( <input

    type="text" placeholder={this.props.placeholder} /> ) } } email
  46. Uncontrolled component

  47. class TextInput extends React.Component { render() { return ( <input

    type="text" placeholder={this.props.placeholder} /> ) } } email SIDDHARTH
  48. class TextInput extends React.Component { state = { value: ''

    } render() { return ( <input value={this.state.value} type="text" placeholder={this.props.placeholder} /> ) } } email
  49. class TextInput extends React.Component { state = { value: ''

    } render() { return ( <input value={this.state.value} type="text" placeholder={this.props.placeholder} /> ) } } email s
  50. class TextInput extends React.Component { state = { value: ''

    } render() { return ( <input value={this.state.value} type="text" placeholder={this.props.placeholder} /> ) } } email s
  51. class TextInput extends React.Component { state = { value: ''

    } onChange = (event) => { this.setState({ value: event.target.value.toLowerCase() }) } render() { return ( <input onChange={this.onChange} value={this.state.value} type="text" placeholder={this.props.placeholder} /> ) } } email
  52. class TextInput extends React.Component { state = { value: ''

    } onChange = (event) => { this.setState({ value: event.target.value.toLowerCase() }) } render() { return ( <input onChange={this.onChange} value={this.state.value} type="text" placeholder={this.props.placeholder} /> ) } } email siddharth@gmail.com SIDDHARTH@Gmail.com
  53. 0 chars email siddharth@gmail.com address

  54. render( <TextArea placeholder="Enter some text" /> ) address

  55. render( withLabel(‘address', <TextArea placeholder="Enter some text" />) ) address

  56. const withLabel = (label, Component) => { } render( withLabel(‘address',

    <TextArea placeholder="Enter some text" />) ) address
  57. const withLabel = (label, Component) => { return ( <div

    className="form-field"> <label>{label}</label> <Component/> </div> ) } render( withLabel(‘address', <TextArea placeholder="Enter some text" />) ) address
  58. Higher Order Component

  59. const withLabel = (label, Component) => { return ( <div

    className="form-field"> <label>{label}</label> <Component/> </div> ) } render( withLabel(‘address', <TextArea placeholder="Enter some text" />) ) address
  60. const withLabel = (label, Component) => { return ( <div

    className="form-field"> <label>{label}</label> <Component/> </div> ) } render( <WithLabel label="address"> <TextArea placeholder="Enter some text" /> </WithLabel> ) address
  61. const WithLabel = (props) => { return ( <div className="form-field">

    <label>{label}</label> <Component/> </div> ) } render( <WithLabel label="address"> <TextArea placeholder="Enter some text" /> </WithLabel> ) address
  62. const withLabel = (props) => { return ( <div className="form-field">

    <label>{label}</label> {props.children} </div> ) } render( <WithLabel label="address"> <TextArea placeholder="Enter some text" /> </WithLabel> ) address
  63. address accept? email

  64. render( <form> </form> ) email

  65. render( <form> <WithLabel label="address"> <TextInput placeholder="Enter some text" /> </WithLabel>

    </form> ) email
  66. render( <form> <WithLabel label="address"> <TextInput placeholder="Enter some text" /> </WithLabel>

    <WithLabel label="address"> <TextArea placeholder="Enter some text" /> </WithLabel> <WithLabel label="accept?"> <Switch /> </WithLabel> </form> ) email
  67. render( <form> <WithLabel label="address"> <TextInput placeholder="Enter some text" /> </WithLabel>

    <WithLabel label="address"> <TextArea placeholder="Enter some text" /> </WithLabel> <WithLabel label="accept?"> <Switch /> </WithLabel> <div className="actions"> <Button primary onClick={this.save}>Save</Button> <Button onClick={this.clear}>Clear</Button> </div> </form> ) email
  68. address accept? email

  69. render( <form> <WithLabel label="address"> <TextInput placeholder="Enter some text" /> </WithLabel>

    <WithLabel label="address"> <Form.TextArea placeholder="Enter some text" /> </WithLabel> <WithLabel label=“accept?"> <Form.Switch /> </WithLabel> <div className="actions"> <Button primary onClick={this.save}>Save</Button> <Button onClick={this.clear}>Clear</Button> </div> </form> ) email
  70. render( <Form> <Form.TextInput label="email" placeholder="Enter some text" /> <Form.TextArea label="address"

    placeholder="Enter some text" /> <Form.Switch label="accept?" /> <Form.Actions primary={{ label: 'Save Changes', method: this.save }} secondary={{ label: 'Save Changes', method: this.save }} /> </Form> ) email
  71. Compound Component❤

  72. compo
 compo

  73. render( <Form> <Form.TextInput label="email" placeholder="Enter some text" /> <Form.TextArea label="address"

    placeholder="Enter some text" /> <Form.Switch label="accept?" /> <Form.Actions primary={{ label: 'Save Changes', method: this.save }} secondary={{ label: 'Save Changes', method: this.save }} /> </Form> ) email
  74. <Form> <Form.TextInput label="email" placeholder="Enter some text" /> <Form.TextArea label="address" placeholder="Enter

    some text" /> email
  75. const Form = (props) => { return (<form>{ props.children }</form>)

    } <Form.TextInput label="email" placeholder="Enter some text" /> <Form.TextArea label="address" placeholder="Enter some text" /> email
  76. const Form = (props) => { return (<form>{ props.children }</form>)

    } Form.TextInput = props => { return ( <WithLabel label={props.label}> <TextInput placeholder={props.placeholder} /> </WithLabel> ) } <Form.TextArea label="address" placeholder="Enter some text" /> email
  77. const Form = (props) => { return (<form>{ props.children }</form>)

    } Form.TextInput = props => { return ( <WithLabel label={props.label}> <TextInput placeholder={props.placeholder} /> </WithLabel> ) } Form.TextArea = props => { return ( <WithLabel label={props.label}> <TextArea placeholder={props.placeholder} /> </WithLabel> email
  78. render( <Form> <Form.TextInput label="email" placeholder="Enter some text" /> <Form.TextArea label="address"

    placeholder="Enter some text" /> <Form.Switch label="accept?" /> <Form.Actions primary={{ label: 'Save Changes', method: this.save }} secondary={{ label: 'Save Changes', method: this.save }} /> </Form> ) email
  79. email address accept?

  80. render( <Form> <Form.TextInput label="email" placeholder="Enter some text" /> <Form.TextArea label="address"

    placeholder="Enter some text" /> <Form.Switch label="accept?" /> <Form.Actions primary={{ label: 'Save Changes', method: this.save }} secondary={{ label: 'Save Changes', method: this.save }} /> </Form> ) email
  81. email address accept?

  82. email address accept?

  83. 0 chars

  84. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars
  85. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { constructor(props) { ... } onChange(event) { this.setState({ length: event.target.value.length }) } render() { return ( <div> <input onChange={this.onChange.bind(this) /> <span>{this.state.length} chars</span> </div> ) } }
  86. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { constructor(props) { ... } onChange(event) { const error = event.target.value.length > 30 this.setState({ length: event.target.value.length, error }) } render() { return ( <div> <input onChange={this.onChange.bind(this) /> <span className={...}>{this.state.length} chars</span> </div> ) } }
  87. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { constructor(props) { ... } onChange(event) { const error = event.target.value.length > this.props.limit this.setState({ length: event.target.value.length, error }) } render() { return ( <div> <input onChange={this.onChange.bind(this) /> <span className={...}>{this.state.length} chars</span> </div> ) } }
  88. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { ... } render(<TextInput limit={30} />)
  89. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars 0 chars Abc really long name, omg goes on 28 chars 2/3
  90. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { ... } render(<TextInput limit={30} />)
  91. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { ... } render(<TextInput renderLabel={ renderLabel } />)
  92. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { ... } render(<TextInput renderLabel={ renderLabel } />) const renderLabel = (length) => { return <span> {length} chars </span> }
  93. Render prop

  94. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { ... } render(<TextInput renderLabel={ renderLabel } />) const renderLabel = (length) => { return <span> {length} chars </span> }
  95. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { ... } render(<TextInput renderLabel={ renderLabel } />) const renderLabel = (length) => { let className if (length > 25) className = 'warn' else if (length > 30) className = 'error' return <span className={className}> {length} chars </span> }
  96. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { constructor(props) { ... } onChange(event) { ... } render() { return ( <div> <input onChange={this.onChange.bind(this) /> <span>{this.state.length} chars</span> </div> ) } }
  97. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { constructor(props) { ... } onChange(event) { ... } render() { return ( <div> <input onChange={this.onChange.bind(this) /> { this.props.renderLabel() } </div> ) } }
  98. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { constructor(props) { ... } onChange(event) { ... } render() { return ( <div> <input onChange={this.onChange.bind(this) /> { this.props.renderLabel(this.state.length) } </div> ) } }
  99. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { constructor(props) { ... } onChange(event) { ... } render() { return ( this.props.render(this.state.length) ) } }
  100. None
  101. None
  102. None
  103. None
  104. None
  105. <Form> <Form.TextInput label="email" placeholder="Enter some text" /> <Form.TextArea label="address" placeholder="Enter

    some text" /> <Form.Switch label=“accept?" theme="manage" /> <Button theme="manage">Save Changes</Button> </Form>
  106. <Form> <Form.TextInput label="email" placeholder="Enter some text" /> <Form.TextArea label="address" placeholder="Enter

    some text" /> <Form.Switch label=“accept?" theme="extend" /> <Button theme="manage">Save Changes</Button> </Form>
  107. <Form> <Form.TextInput label="email" placeholder="Enter some text" /> <Form.TextArea label="address" placeholder="Enter

    some text" /> <Form.Switch label=“accept?" theme="extend" /> <Button theme="manage">Save Changes</Button> </Form>
  108. <Form> <Form.TextInput label="email" placeholder="Enter some text" /> <Form.TextArea label="address" placeholder="Enter

    some text" /> <Form.Switch label=“accept?" theme="extend" /> <Button theme="extend">Save Changes</Button> </Form>
  109. render( <App /> )

  110. render( <ThemeProvider name="manage"> <App /> </ThemeProvider> )

  111. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> )

  112. Provider Component

  113. None
  114. https://avatars3.githubusercontent.com/u/17475736?s=400&v=4 ✅ ✅ ✅

  115. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> )

  116. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> )

  117. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> ) class ThemeProvider extends

    React.Component { render() { return this.props.children } }
  118. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> ) class ThemeProvider extends

    React.Component { getChildContext() { return { theme: this.props.name } } render() { return this.props.children } }
  119. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> ) class ThemeProvider extends

    React.Component { getChildContext() { return { theme: this.props.name } } render() { return this.props.children } } ThemeProvider.childContextTypes = { theme: PropTypes.string }
  120. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> ) class ThemeProvider extends

    React.Component { ... } ThemeProvider.childContextTypes = { theme: PropTypes.string }
  121. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> ) class ThemeProvider extends

    React.Component { ... } ThemeProvider.childContextTypes = { theme: PropTypes.string } const Button = (props) => { return <button className="button">{props.children}</button> }
  122. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> ) class ThemeProvider extends

    React.Component { ... } ThemeProvider.childContextTypes = { theme: PropTypes.string } const Button = (props) => { return <button className="button">{props.children}</button> } Button.contextTypes = { theme: PropTypes.string }
  123. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> ) class ThemeProvider extends

    React.Component { ... } ThemeProvider.childContextTypes = { theme: PropTypes.string } const Button = (props, context) => { return <button className="button">{props.children}</button> } Button.contextTypes = { theme: PropTypes.string }
  124. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> ) class ThemeProvider extends

    React.Component { ... } ThemeProvider.childContextTypes = { theme: PropTypes.string } const Button = (props, context) => { let className = 'button' + 'button-' + context.theme return <button className={className}>{props.children}</button> } Button.contextTypes = { theme: PropTypes.string }
  125. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> )

  126. render( <ThemeProvider name="manage"> <App /> </ThemeProvider> )

  127. None
  128. class ThemeProvider extends React.Component { ... } ThemeProvider.childContextTypes = {

    theme: PropTypes.string } const Button = (props, context) => { let className = 'button' + 'button-' + context.theme return <button className={className}>{props.children}</button> } Button.contextTypes = { theme: PropTypes.string } render( <ThemeProvider name="extend"> <App /> </ThemeProvider> )
  129. class ThemeProvider extends React.Component { ... } ThemeProvider.childContextTypes = {

    theme: PropTypes.string } const Button = (props, context) => { let className = 'button' + 'button-' + context.theme return <button className={className}>{props.children}</button> } addContext(Button) render( <ThemeProvider name="extend"> <App /> </ThemeProvider> )
  130. class ThemeProvider extends React.Component { ... } ThemeProvider.childContextTypes = {

    theme: PropTypes.string } export function addContext (Component) { Component['contextTypes'] = { theme: PropTypes.string } return Component } ----- const Button = (props, context) => { let className = 'button' + 'button-' + context.theme return <button className={className}>{props.children}</button> } addContext(Button)
  131. 7 component patterns

  132. I hope you learned something today

  133. siddharthkp