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

Add Superpowers to your React components using ES7 decorators

Add Superpowers to your React components using ES7 decorators

Siddharth Kshetrapal

January 14, 2017
Tweet

More Decks by Siddharth Kshetrapal

Other Decks in Technology

Transcript

  1. add superpowers to your React components with
    ES7 decorators

    View full-size slide

  2. javascript architect @ practo
    @siddharthkp

    View full-size slide

  3. ES7 decorators
    stage 2
    https://tc39.github.io/proposal-decorators

    View full-size slide

  4. https://babeljs.io/docs/plugins/transform-decorators/
    npm install babel-plugin-transform-decorators-legacy --save

    View full-size slide

  5. but, first
    template literals
    ES 6 Classes
    arrow functions

    View full-size slide

  6. const firstName = 'siddharth'
    const lastName = 'kshetrapal'
    const fullName = firstName + ' ' + lastName
    Template literals

    View full-size slide

  7. const firstName = 'siddharth'
    const lastName = 'kshetrapal'
    const fullName = firstName + ' ' + lastName
    const fullName = `${firstName} ${lastName}`
    Template literals

    View full-size slide

  8. class Human {
    }
    ES 6 classes

    View full-size slide

  9. class Human {
    constructor(name) {
    this.name = name
    }
    speak() {
    console.log(`Hi! My name is ${this.name}`)
    }
    }
    ES 6 classes

    View full-size slide

  10. class Human {
    constructor(name) {
    this.name = name
    }
    speak() {
    console.log(`Hi! My name is ${this.name}`)
    }
    }
    const siddharth = new Human('siddharth')
    siddharth.speak() // Hi! My name is siddharth
    ES 6 classes

    View full-size slide

  11. function log(message) {
    return console.log(message)
    }
    arrow functions

    View full-size slide

  12. function log(message) {
    return console.log(message)
    }
    const log = (message) => {
    return console.log(message)
    }
    arrow functions

    View full-size slide

  13. function log(message) {
    return console.log(message)
    }
    const log = message => {
    return console.log(message)
    }
    arrow functions

    View full-size slide

  14. function log(message) {
    return console.log(message)
    }
    const log = message => console.log(message)
    arrow functions

    View full-size slide

  15. function log(message) {
    return function () {
    return console.log(message)
    }
    }
    arrow functions

    View full-size slide

  16. arrow functions
    function log(message) {
    return function () {
    return console.log(message)
    }
    }
    const log = message => {
    return () => {
    return console.log(message)
    }
    }

    View full-size slide

  17. arrow functions
    function log(message) {
    return function () {
    return console.log(message)
    }
    }
    const log = message => {
    return () => console.log(message)
    }

    View full-size slide

  18. arrow functions
    function log(message) {
    return function () {
    return console.log(message)
    }
    }
    const log = message => () => console.log(message)

    View full-size slide

  19. ES7 decorators
    and we’re back
    https://tc39.github.io/proposal-decorators

    View full-size slide

  20. class decorators
    class method decorators
    ES7 decorators

    View full-size slide

  21. syntax
    class Title extends React.Component {
    render () { return hello {this.props.name} }
    }

    View full-size slide

  22. class className {
    func() {}
    }
    syntax

    View full-size slide

  23. @class-decorator
    class className {
    @class-method-decorator
    func() {}
    }
    syntax

    View full-size slide

  24. class className {
    @class-method-decorator
    func() {}
    }
    class method decorators

    View full-size slide

  25. class Human {
    constructor(name) {
    this.name = name
    }
    speak() {
    console.log(`Hi! My name is ${this.name}`)
    }
    }

    View full-size slide

  26. class Human {
    constructor(name) {
    this.name = name
    }
    speak() {
    console.log(`Hi! My name is ${this.name}`)
    }
    }
    const siddharth = new Human('siddharth')
    siddharth.speak() // Hi! My name is siddharth

    View full-size slide

  27. ...
    speak() {
    console.log(`Hi! My name is ${this.name}`)
    }
    }
    const siddharth = new Human('siddharth')
    siddharth.speak = () => {console.log('Hi! My name is dum dum')

    View full-size slide

  28. ...
    speak() {
    console.log(`Hi! My name is ${this.name}`)
    }
    }
    const siddharth = new Human('siddharth')
    siddharth.speak = () => {console.log('Hi! My name is dum dum')
    siddharth.speak() // Hi! My name is dum dum

    View full-size slide

  29. class Human {
    constructor(name) {
    this.name = name
    }
    @readonly
    speak() {
    console.log(`Hi! My name is ${this.name}`)
    }
    }

    View full-size slide

  30. syntax
    @decorator = function
    const decorator = (target, key, descriptor) => {
    }

    View full-size slide

  31. const readonly = (target, key, descriptor) => {
    }

    View full-size slide

  32. const readonly = (target, key, descriptor) => {
    console.log(target, key, descriptor)
    }
    /*
    Human {},
    'speak',
    { value: [Function: speak],
    writable: true,
    enumerable: false,
    configurable: true }
    */

    View full-size slide

  33. const readonly = (target, key, descriptor) => {
    descriptor.writable = false
    return descriptor
    }

    View full-size slide

  34. @readonly
    speak() {
    console.log(`Hi! My name is ${this.name}`)
    }
    }
    const siddharth = new Human('siddharth')
    siddharth.speak = () => {console.log('Hi! My name is dum dum'}
    siddharth.speak() // Hi! My name is siddharth

    View full-size slide

  35. class Human {
    constructor(name) {this.name = name}
    useAngular() {
    console.log('writing angular code')
    }
    }
    siddharth.writeAngular()
    // writing angular code

    View full-size slide

  36. class Human {
    constructor(name) {this.name = name}
    @deprecate
    useAngular() {
    console.log('writing angular code')
    }
    }
    siddharth.writeAngular()
    // speak@Human: this feature will be deprecated soon!
    // writing angular code

    View full-size slide

  37. const deprecate = (target, key, descriptor) => {
    }

    View full-size slide

  38. const deprecate = (target, key, descriptor) => {
    const original = descriptor.value
    const name = `${target.constructor.name}.${key}`
    }

    View full-size slide

  39. const deprecate = (target, key, descriptor) => {
    const original = descriptor.value
    const name = `${target.constructor.name}.${key}`
    const message = 'this feature will be deprecated soon!'
    descriptor.value = () => {
    console.log(`${name} : ${message}`)
    original.apply(this, arguments)
    }
    }

    View full-size slide

  40. const deprecate = (target, key, descriptor) => {
    const original = descriptor.value
    const name = `${target.constructor.name}.${key}`
    const message = 'this feature will be deprecated soon!'
    descriptor.value = () => {
    console.log(`${name} : ${message}`)
    original.apply(this, arguments)
    }
    return descriptor
    }

    View full-size slide

  41. class Human {
    constructor(name) {this.name = name}
    @deprecate
    useAngular() {
    console.log('writing angular code')
    }
    }

    View full-size slide

  42. class Human {
    constructor(name) {this.name = name}
    @deprecate
    useAngular() {
    console.log('writing angular code')
    }
    }
    siddharth.writeAngular()
    // speak@Human: this feature will be deprecated soon!
    // writing angular code

    View full-size slide

  43. class Human {
    constructor(name) {this.name = name}
    @deprecate('will be deprecated in v2.0.0')
    useAngular() {
    console.log('writing angular code')
    }
    }
    siddharth.writeAngular()
    // speak@Human: will be deprecated in v2.0.0
    // writing angular code

    View full-size slide

  44. syntax
    @decorator = function
    const decorator = (target, key, descriptor) => {
    }
    @decorator(argument) = higher order function
    const decorator = (argument) => {
    return (target, key, descriptor) => {
    }
    }

    View full-size slide

  45. syntax
    @decorator = function
    const decorator = (target, key, descriptor) => {
    }
    @decorator(argument) = higher order function
    const decorator = argument => (target, key, descriptor) => {
    }

    View full-size slide

  46. @deprecate = function
    const deprecate = (target, key, descriptor) => {
    const original = descriptor.value
    const name = `${target.constructor.name}.${key}`
    const message = 'this feature will be deprecated soon!'
    descriptor.value = () => {
    console.log(`${name} : ${message}`)
    original.apply(this, arguments)
    }
    return descriptor
    }

    View full-size slide

  47. @deprecate(message) = higher order function
    const deprecate = message => (target, key, descriptor) => {
    const original = descriptor.value
    const name = `${target.constructor.name}.${key}`
    const message = 'this feature will be deprecated soon!'
    descriptor.value = () => {
    console.log(`${name} : ${message}`)
    original.apply(this, arguments)
    }
    return descriptor
    }

    View full-size slide

  48. @deprecate(message) = higher order function
    const deprecate = message => (target, key, descriptor) => {
    const original = descriptor.value
    const name = `${target.constructor.name}.${key}`
    message = message || 'this feature will be deprecated soon!'
    descriptor.value = () => {
    console.log(`${name} : ${message}`)
    original.apply(this, arguments)
    }
    return descriptor
    }

    View full-size slide

  49. don’t fret

    View full-size slide

  50. jayphelps/core-decorators
    npm install core-decorators --save

    View full-size slide

  51. import {readonly} from 'core-decorators'
    class Human {
    constructor(name) {
    this.name = name
    }
    @readonly
    speak() {
    console.log('Hi! My name is ' + this.name)
    }
    }
    core-decorators

    View full-size slide

  52. import {time} from 'core-decorators'
    class Human {
    constructor(name) {
    this.name = name
    }
    @time('speak') // Human.speak-0: 1.582ms
    speak() {
    console.log('Hi! My name is ' + this.name)
    }
    }
    core-decorators

    View full-size slide

  53. class decorators
    class method decorators
    ES7 decorators

    View full-size slide

  54. @class-decorator
    class className {
    func() {}
    }
    class decorators

    View full-size slide

  55. @type('mammal')
    class Human {
    constructor(name) {
    this.name = name
    console.log('I am' + this.name + ', a ' + this.type)
    }
    }
    /*
    class Dolphin
    class Crocodile
    class Fish
    */
    class decorators

    View full-size slide

  56. @type('mammal')
    class Human {
    constructor(name) {
    this.name = name
    console.log('I am' + this.name + ', a ' + this.type)
    }
    }
    const type = value => target => {
    // add {type: value} to the target class
    }
    class decorators

    View full-size slide

  57. HOC: design pattern
    higher order components

    View full-size slide

  58. A higher-order component is a function that takes a component and
    returns a new (modified) component.
    class Label extends React.Component

    View full-size slide

  59. HOC = class decorators

    View full-size slide

  60. class Title extends React.Component {
    render () { return hello {this.props.name} }
    }

    View full-size slide

  61. class Title extends React.Component {
    render () { return hello {this.props.name} }
    }
    /*



    /*

    View full-size slide

  62. @loading('name')
    class Title extends React.Component {
    render () { return hello {this.props.name} }
    }
    /*



    /*
    HOC

    View full-size slide

  63. @loading('name')
    class Title extends React.Component {
    render () { return hello {this.props.name} }
    }
    const loading = key => target => class extends React.Component {
    }
    HOC

    View full-size slide

  64. @loading('name')
    class Title extends React.Component {
    render () { return hello {this.props.name} }
    }
    const loading = key => Component => class extends React.Component {
    }
    HOC

    View full-size slide

  65. @loading('name')
    class Title extends React.Component {
    render () { return hello {this.props.name} }
    }
    const loading = key => Component => class extends React.Component {
    render () {
    if (this.props[key]) return else return loading...
    }
    }
    HOC

    View full-size slide

  66. https://github.com/acdlite/recompose
    npm install recompose --save

    View full-size slide

  67. import {withProps} from 'recompose'
    recompose

    View full-size slide

  68. import {withProps} from 'recompose'
    @withProps({name: 'world'})
    class Title extends React.Component {
    render () { return hello {this.props.name} }
    }
    recompose

    View full-size slide

  69. import {withHandlers, compose} from 'recompose'
    const handlers = withHandlers({
    onClick: props => event => mixpanel.track('Clicked', props)
    })
    recompose

    View full-size slide

  70. import {withHandlers, compose} from 'recompose'
    const handlers = withHandlers({
    onClick: props => event => mixpanel.track('Clicked', props)
    })
    const analytics = compose(handlers)
    recompose

    View full-size slide

  71. import {withHandlers, compose} from 'recompose'
    const handlers = withHandlers({
    onClick: props => event => mixpanel.track('Clicked', props)
    })
    const analytics = compose(handlers)
    @analytics
    class Title extends React.Component {
    render () { return hello {this.props.label} }
    }
    recompose

    View full-size slide

  72. import {onlyUpdateForKeys} from 'recompose'
    @onlyUpdateForKeys(['name'])
    class Title extends React.Component {
    render () { return hello {this.props.name} }
    }
    /*



    /*
    recompose

    View full-size slide

  73. class Title extends React.Component {
    constructor () {} /* javascript constructor */
    css () {} /* css constructor ? */
    render () { return hello {this.props.name} }
    }
    rant/wish

    View full-size slide

  74. https://github.com/siddharthkp/css-constructor
    npm install css-constructor --save

    View full-size slide

  75. import css from 'css-constructor'
    class Title extends React.Component {
    constructor () {}
    @css`
    font-size: 16px;
    color: {this.props.colors};
    &:hover {color: #FFF;}
    @media {max-width: 480px} {&: font-size: 18px;}
    `
    render () { return hello {this.props.name} }
    }
    css-constructor

    View full-size slide

  76. @css(styles) = higher order function
    const css = styles => (target, key, descriptor) => {
    }
    css-constructor

    View full-size slide

  77. @css(styles) = higher order function
    const css = styles => (target, key, descriptor) => {
    const prettyCSS = process(styles) // fill props, media queries, etc.
    const cssClass = getUniqueClassName(prettyCSS)
    document.head.styles += {cssClass: prettyCSS}
    return componentWithClassName(cssClass)
    }
    css-constructor

    View full-size slide

  78. @siddharthkp

    View full-size slide