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

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.

Siddharth Kshetrapal

January 19, 2018
Tweet

More Decks by Siddharth Kshetrapal

Other Decks in Technology

Transcript

  1. Advanced
    Component
    Patterns

    View full-size slide

  2. I teach React

    View full-size slide

  3. MUST LEARN ALL PATTERNS

    View full-size slide

  4. 7 component patterns

    View full-size slide

  5. DESIGN DEVELOPMENT

    View full-size slide

  6. const TextInput = props => {
    return (
    className="input"
    type="text"
    placeholder={props.placeholder}
    />
    )
    }
    render(

    )

    View full-size slide

  7. const TextInput = props => {
    return (
    className="input"
    type="text"
    placeholder={props.placeholder}
    />
    )
    }
    render(

    )

    View full-size slide

  8. const TextInput = props => {
    return (
    className="input"
    type="text"
    placeholder={props.placeholder}
    />
    )
    }
    render(

    )

    View full-size slide

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

    )

    View full-size slide

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

    )

    View full-size slide

  11. Functional component

    View full-size slide

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

    )

    View full-size slide

  13. const TextInput = props => {
    return (
    className="input"
    type="text"
    placeholder={props.placeholder}
    />
    )
    }
    0 chars

    View full-size slide

  14. const TextInput = props => {
    return (

    0 chars
    className="input"
    type="text"
    placeholder={props.placeholder}
    />

    )
    }
    0 chars

    View full-size slide

  15. class TextInput extends React.Component {
    render() {
    return (

    0 chars
    className="input"
    type="text"
    placeholder={props.placeholder}
    />

    )
    }
    }
    0 chars

    View full-size slide

  16. class TextInput extends React.Component {
    render() {
    return (

    0 chars
    className="input"
    type="text"
    placeholder={this.props.placeholder}
    />

    )
    }
    }
    0 chars

    View full-size slide

  17. class TextInput extends React.Component {
    constructor(props) {
    super(props)
    this.state = { length: 0 }
    }
    render() {
    return (

    0 chars
    className="input"
    type="text"
    placeholder={this.props.placeholder}
    />

    )
    }
    0 chars

    View full-size slide

  18. class TextInput extends React.Component {
    constructor(props) {
    super(props)
    this.state = { length: 0 }
    }
    render() {
    return (

    {this.state.length} chars
    className="input"
    type="text"
    placeholder={this.props.placeholder}
    />

    )
    }
    0 chars

    View full-size slide

  19. class TextInput extends React.Component {
    constructor(props) { ... }
    render() {
    return (

    {this.state.length} chars
    className="input"
    type="text"
    placeholder={this.props.placeholder}
    />

    )
    }
    0 chars

    View full-size slide

  20. class TextInput extends React.Component {
    constructor(props) { ... }
    onChange(event) {
    this.setState({ length: event.target.value.length })
    }
    render() {
    return (

    {this.state.length} chars
    className="input"
    type="text"
    placeholder={this.props.placeholder}
    />

    )
    }
    0 chars

    View full-size slide

  21. class TextInput extends React.Component {
    constructor(props) { ... }
    onChange(event) {
    this.setState({ length: event.target.value.length })
    }
    render() {
    return (

    {this.state.length} chars
    className="input"
    type="text"
    placeholder={this.props.placeholder
    onChange={this.onChange.bind(this)}
    />

    )
    0 chars Abc
    3 chars

    View full-size slide

  22. Class component

    View full-size slide

  23. class TextInput extends React.Component {
    constructor(props) { ... }
    onChange(event) {
    this.setState({ length: event.target.value.length })
    }
    render() {
    return (

    {this.state.length} chars
    className="input"
    type="text"
    placeholder={this.props.placeholder
    onChange={this.onChange.bind(this)}
    />

    )
    0 chars Abc
    3 chars

    View full-size slide

  24. class TextInput extends React.Component {
    constructor(props) { ... }
    onChange(event) {
    this.setState({ length: event.target.value.length })
    }
    render() {
    return (

    {this.state.length} chars
    className="input"
    type="text"
    placeholder={this.props.placeholder
    onChange={this.onChange.bind(this)}
    />

    )
    0 chars Abc
    3 chars

    View full-size slide

  25. class TextInput extends React.Component {
    constructor(props) { ... }
    onChange(event) {
    this.setState({ length: event.target.value.length })
    }
    render() {
    return (

    {this.state.length} chars
    className="input"
    type="text"
    placeholder={this.props.placeholder
    onChange={this.onChange}
    />

    )
    0 chars Abc
    3 chars

    View full-size slide

  26. class TextInput extends React.Component {
    constructor(props) { ... }
    onChange = (event) => {
    this.setState({ length: event.target.value.length })
    }
    render() {
    return (

    {this.state.length} chars
    className="input"
    type="text"
    placeholder={this.props.placeholder
    onChange={this.onChange}
    />

    )
    0 chars Abc
    3 chars

    View full-size slide

  27. class TextInput extends React.Component {
    constructor(props) { ... }
    onChange = (event) => { ... }
    render() {
    return (

    {this.state.length} chars
    className="input"
    type="text"
    placeholder={this.props.placeholder
    onChange={this.onChange}
    />

    0 chars Abc
    3 chars

    View full-size slide

  28. class TextInput extends React.Component {
    constructor(props) {
    super(props)
    this.state = { length: 0 }
    }
    onChange = (event) => { ... }
    render() {
    return (

    {this.state.length} chars
    className="input"
    type="text"
    placeholder={this.props.placeholder
    onChange={this.onChange}
    />

    0 chars Abc
    3 chars

    View full-size slide

  29. class TextInput extends React.Component {
    state = { length: 0 }
    onChange = (event) => { ... }
    render() {
    return (

    {this.state.length} chars
    className="input"
    type="text"
    placeholder={this.props.placeholder
    onChange={this.onChange}
    />

    0 chars Abc
    3 chars

    View full-size slide

  30. class TextInput extends React.Component {
    state = { length: 0 }
    onChange = (event) => { ... }
    render() {
    return (

    {this.state.length} chars
    className="input"
    type="text"
    placeholder={this.props.placeholder
    onChange={this.onChange}
    />

    )
    }
    }
    0 chars Abc
    3 chars

    View full-size slide

  31. class TextInput extends React.Component {
    state = { length: 0 }
    onChange = (event) => { ... }
    render() {
    return (

    {this.state.length} chars
    className="input"
    type="text"
    placeholder={this.props.placeholder
    onChange={this.onChange}
    />

    )
    }
    }
    0 chars Abc
    3 chars

    View full-size slide

  32. class TextInput extends React.Component {
    render() {
    return (
    type="text"
    placeholder={this.props.placeholder}
    />
    )
    }
    }
    email

    View full-size slide

  33. Uncontrolled component

    View full-size slide

  34. class TextInput extends React.Component {
    render() {
    return (
    type="text"
    placeholder={this.props.placeholder}
    />
    )
    }
    }
    email SIDDHARTH

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  40. render(

    )
    address

    View full-size slide

  41. render(
    withLabel(‘address', )
    )
    address

    View full-size slide

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

    View full-size slide

  43. const withLabel = (label, Component) => {
    return (

    {label}


    )
    }
    render(
    withLabel(‘address', )
    )
    address

    View full-size slide

  44. Higher Order Component

    View full-size slide

  45. const withLabel = (label, Component) => {
    return (

    {label}


    )
    }
    render(
    withLabel(‘address', )
    )
    address

    View full-size slide

  46. const withLabel = (label, Component) => {
    return (

    {label}


    )
    }
    render(



    )
    address

    View full-size slide

  47. const WithLabel = (props) => {
    return (

    {label}


    )
    }
    render(



    )
    address

    View full-size slide

  48. const withLabel = (props) => {
    return (

    {label}
    {props.children}

    )
    }
    render(



    )
    address

    View full-size slide

  49. address
    accept?
    email

    View full-size slide

  50. render(


    )
    email

    View full-size slide

  51. render(





    )
    email

    View full-size slide

  52. render(











    )
    email

    View full-size slide

  53. render(











    Save
    Clear


    )
    email

    View full-size slide

  54. address
    accept?
    email

    View full-size slide

  55. render(











    Save
    Clear


    )
    email

    View full-size slide

  56. render(




    primary={{ label: 'Save Changes', method: this.save }}
    secondary={{ label: 'Save Changes', method: this.save }}
    />

    )
    email

    View full-size slide

  57. Compound Component❤

    View full-size slide

  58. compo

    compo

    View full-size slide

  59. render(




    primary={{ label: 'Save Changes', method: this.save }}
    secondary={{ label: 'Save Changes', method: this.save }}
    />

    )
    email

    View full-size slide

  60. const Form = (props) => {
    return ({ props.children })
    }


    email

    View full-size slide

  61. const Form = (props) => {
    return ({ props.children })
    }
    Form.TextInput = props => {
    return (



    )
    }

    email

    View full-size slide

  62. const Form = (props) => {
    return ({ props.children })
    }
    Form.TextInput = props => {
    return (



    )
    }
    Form.TextArea = props => {
    return (



    email

    View full-size slide

  63. render(




    primary={{ label: 'Save Changes', method: this.save }}
    secondary={{ label: 'Save Changes', method: this.save }}
    />

    )
    email

    View full-size slide

  64. email
    address
    accept?

    View full-size slide

  65. render(




    primary={{ label: 'Save Changes', method: this.save }}
    secondary={{ label: 'Save Changes', method: this.save }}
    />

    )
    email

    View full-size slide

  66. email
    address
    accept?

    View full-size slide

  67. email
    address
    accept?

    View full-size slide

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

    View full-size slide

  69. 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 (


    {this.state.length} chars

    )
    }
    }

    View full-size slide

  70. 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 (


    {this.state.length} chars

    )
    }
    }

    View full-size slide

  71. 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 (


    {this.state.length} chars

    )
    }
    }

    View full-size slide

  72. 0 chars
    PROJECT NAME
    Abc really long name, omg goes on and on 34 chars
    class TextInput extends React.Component {
    ...
    }
    render()

    View full-size slide

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

    View full-size slide

  74. 0 chars
    PROJECT NAME
    Abc really long name, omg goes on and on 34 chars
    class TextInput extends React.Component {
    ...
    }
    render()

    View full-size slide

  75. 0 chars
    PROJECT NAME
    Abc really long name, omg goes on and on 34 chars
    class TextInput extends React.Component {
    ...
    }
    render()

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  79. 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.state.length} chars

    )
    }
    }

    View full-size slide

  80. 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.renderLabel() }

    )
    }
    }

    View full-size slide

  81. 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.renderLabel(this.state.length) }

    )
    }
    }

    View full-size slide

  82. 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)
    )
    }
    }

    View full-size slide





  83. Save Changes

    View full-size slide





  84. Save Changes

    View full-size slide





  85. Save Changes

    View full-size slide





  86. Save Changes

    View full-size slide

  87. Provider Component

    View full-size slide

  88. https://avatars3.githubusercontent.com/u/17475736?s=400&v=4





    View full-size slide

  89. render(



    )
    class ThemeProvider extends React.Component {
    render() {
    return this.props.children
    }
    }

    View full-size slide

  90. render(



    )
    class ThemeProvider extends React.Component {
    getChildContext() {
    return { theme: this.props.name }
    }
    render() {
    return this.props.children
    }
    }

    View full-size slide

  91. render(



    )
    class ThemeProvider extends React.Component {
    getChildContext() {
    return { theme: this.props.name }
    }
    render() {
    return this.props.children
    }
    }
    ThemeProvider.childContextTypes = {
    theme: PropTypes.string
    }

    View full-size slide

  92. render(



    )
    class ThemeProvider extends React.Component {
    ...
    }
    ThemeProvider.childContextTypes = {
    theme: PropTypes.string
    }

    View full-size slide

  93. render(



    )
    class ThemeProvider extends React.Component {
    ...
    }
    ThemeProvider.childContextTypes = {
    theme: PropTypes.string
    }
    const Button = (props) => {
    return {props.children}
    }

    View full-size slide

  94. render(



    )
    class ThemeProvider extends React.Component {
    ...
    }
    ThemeProvider.childContextTypes = {
    theme: PropTypes.string
    }
    const Button = (props) => {
    return {props.children}
    }
    Button.contextTypes = {
    theme: PropTypes.string
    }

    View full-size slide

  95. render(



    )
    class ThemeProvider extends React.Component {
    ...
    }
    ThemeProvider.childContextTypes = {
    theme: PropTypes.string
    }
    const Button = (props, context) => {
    return {props.children}
    }
    Button.contextTypes = {
    theme: PropTypes.string
    }

    View full-size slide

  96. render(



    )
    class ThemeProvider extends React.Component {
    ...
    }
    ThemeProvider.childContextTypes = {
    theme: PropTypes.string
    }
    const Button = (props, context) => {
    let className = 'button' + 'button-' + context.theme
    return {props.children}
    }
    Button.contextTypes = {
    theme: PropTypes.string
    }

    View full-size slide

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



    )

    View full-size slide

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



    )

    View full-size slide

  99. 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 {props.children}
    }
    addContext(Button)

    View full-size slide

  100. 7 component patterns

    View full-size slide

  101. I hope you learned something today

    View full-size slide