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. const TextInput = props => { return ( <input className="input"

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

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

    type="text" placeholder={props.placeholder} /> ) } render( <TextInput readOnly placeholder="Enter some text" /> )
  4. 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" /> )
  5. 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" /> )
  6. 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" /> )
  7. const TextInput = props => { return ( <input className="input"

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

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

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

    <span>0 chars</span> <input className="input" type="text" placeholder={this.props.placeholder} /> </div> ) } } 0 chars
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 1/3

  25. 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
  26. class TextInput extends React.Component { render() { return ( <input

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

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

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

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

    } render() { return ( <input value={this.state.value} type="text" placeholder={this.props.placeholder} /> ) } } email s
  31. 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
  32. 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 [email protected] [email protected]
  33. const withLabel = (label, Component) => { } render( withLabel(‘address',

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

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

    className="form-field"> <label>{label}</label> <Component/> </div> ) } render( withLabel(‘address', <TextArea placeholder="Enter some text" />) ) address
  36. 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
  37. const WithLabel = (props) => { return ( <div className="form-field">

    <label>{label}</label> <Component/> </div> ) } render( <WithLabel label="address"> <TextArea placeholder="Enter some text" /> </WithLabel> ) address
  38. 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
  39. 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
  40. 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
  41. 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
  42. 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
  43. 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
  44. 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
  45. 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
  46. 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
  47. 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
  48. 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
  49. 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> ) } }
  50. 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> ) } }
  51. 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> ) } }
  52. 0 chars PROJECT NAME Abc really long name, omg goes

    on and on 34 chars class TextInput extends React.Component { ... } render(<TextInput limit={30} />)
  53. 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
  54. 0 chars PROJECT NAME Abc really long name, omg goes

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

    on and on 34 chars class TextInput extends React.Component { ... } render(<TextInput renderLabel={ renderLabel } />)
  56. 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> }
  57. 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> }
  58. 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> }
  59. 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> ) } }
  60. 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> ) } }
  61. 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> ) } }
  62. 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) ) } }
  63. <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>
  64. <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>
  65. <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>
  66. <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>
  67. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> ) class ThemeProvider extends

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

    React.Component { getChildContext() { return { theme: this.props.name } } render() { return this.props.children } }
  69. 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 }
  70. render( <ThemeProvider name="extend"> <App /> </ThemeProvider> ) class ThemeProvider extends

    React.Component { ... } ThemeProvider.childContextTypes = { theme: PropTypes.string }
  71. 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> }
  72. 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 }
  73. 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 }
  74. 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 }
  75. 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> )
  76. 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> )
  77. 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)