New features in ES7 and how we can use them in React

Michał Zdunek

Ecma International

  • The TC-39 group standardizes JavaScript
  • ES6/ES2015 - approved on 17 June 2015
  • ES7/ES2016 - developed concurrently with ES2015
  • 5 stages for proposals - stage 0 to stage 4
  • Only one proposal in stage 4 (Finished)

Using ES7 with Babel

Install Babel with plugins

$ npm install babel-core babel-loader --save-dev
$ npm install babel-plugin-transform-es2015-arrow-functions --save-dev
$ npm install babel-preset-es2015 babel-preset-stage-0 --save-dev

.babelrc

{
  "plugins": ["transform-es2015-arrow-functions"]
  "presets": ["es2015", "stage-0"]
}

webpack.config.js

query: {
  plugins: ['transform-es2015-arrow-functions'],
  presets: ['es2015', 'stage-0'],
}

Decorators on functions

function readonly(target, key, descriptor) {
    descriptor.writable = false;
    return descriptor;
}

class Cat {
    @readonly
    meow() {
        return 'Cat says meow!';
    }
}

var garfield = new Cat();
garfield.meow = function() {
    console.log('I want lasagne!');
}

//Exception: Attempted to assign to readonly property

Decorators on classes

function superhero(target) {
    target.isSuperhero = true;
    target.power = 'flight';
}

@superhero
class MySuperHero() {}

console.log(MySuperHero.isSuperhero); // true

React mixins

var DefaultNameMixin = {
    getDefaultProps: function () {
        return {name: "Skippy"};
    }
};

var ComponentOne = React.createClass({
    mixins: [DefaultNameMixin],
    render: function() {
        return <h2>Hello {this.props.name}</h2>;
    }
});

Higher order components

var DefaultName = ComposedComponent => class extends Component {
  render() {
    return (
      <ComposedComponent 
        {...this.props} 
        name={this.props.name || "Skippy"} 
      />
    );
  }
};

class ComponentOne extends Component {
  render() {
    return <h2>Hello {this.props.name}</h2>;
  }
}

var ComponentOne = DefaultName(ComponentOne);

Mixins as decorators

var DefaultName = ComposedComponent => class extends Component {
  render() {
    return (
      <ComposedComponent 
        {...this.props} 
        name={this.props.name || "Skippy"} 
      />
    );
  }
};

@DefaultName
class ComponentOne extends Component {
  render() {
    return <h2>Hello {this.props.name}</h2>;
  }
}

Object Rest/Spread Properties

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x; // 1
y; // 2
z; // { a: 3, b: 4 }

let n = { x, y, ...z };
n; // { x: 1, y: 2, a: 3, b: 4 }

Transferring props in React

var FancyCheckbox = React.createClass({
  render: function() {
    var { checked, title, ...other } = this.props;
    var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
    var fancyTitle = checked ? 'X ' + title : 'O ' + title;
    return (
      <label>
        <input {...other}
          checked={checked}
          className={fancyClass}
          type="checkbox"
        />
        {fancyTitle}
      </label>
    );
  }
});

Class and instance properties in ES6

export class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
  }
  tick() {
    this.setState({count: this.state.count + 1});
  }
  render() {
    return (
      <div onClick={this.tick.bind(this)}>
        Clicks: {this.state.count}
      </div>
    );
  }
}
Counter.propTypes = { initialCount: React.PropTypes.number };
Counter.defaultProps = { initialCount: 0 };

ES7 property initializers

export class Counter extends React.Component {
  static propTypes = { initialCount: React.PropTypes.number };
  static defaultProps = { initialCount: 0 };
  state = { count: this.props.initialCount };
  tick() {
    this.setState({ count: this.state.count + 1 });
  }
  render() {
    return (
      <div onClick={this.tick.bind(this)}>
        Clicks: {this.state.count}
      </div>
    );
  }
}

ES6 arrow functions

var a = [
  "Hydrogen",
  "Helium",
  "Lithium",
  "Beryl­lium"
];

// Before
var a2 = a.map(function(s){ return s.length });

// After
var a3 = a.map( s => s.length );

Lexical this

function Person() {
  var self = this;
  self.age = 0;

  setInterval(function growUp() {
    self.age++;
  }, 1000);
}
function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++;
  }, 1000);
}

Before

After

Autobinding in ES7

class Counter extends React.Component {
  constructor() {
    super();
    this.tick = this.tick.bind(this);
  }
  tick() { ... }
  ...
}
class Counter extends React.Component {
  tick = () => {
    ...
  }
  ...
}

Before

After

Array.prototype.includes

if (arr.indexOf(el) >= 0) {
    ...
}
if (arr.includes(el)) {
    ...
}

Before

After

Blocking AJAX calls

try {
  var story = getJSONSync('story.json');
  var chapter1 = getJSONSync(story.chapterUrls[0]);
  addHtmlToPage(chapter1.html);
}
catch (e) {
  addTextToPage("Failed to show chapter");
}

document.querySelector('.spinner').style.display = 'none';

Callbacks

getJSON('story.json', (error, story) => {
    if (error) {
        addTextToPage("Failed to show chapter");
        document.querySelector('.spinner').style.display = 'none';
    } else {
        getJSON(story.chapterUrls[0], (error, story) => {
            if(error) {
                addTextToPage("Failed to show chapter");
            } else {
                addHtmlToPage(chapter1.html);
            }
        });
        document.querySelector('.spinner').style.display = 'none';
    }
});

ES6 promises

getJSON('story.json').then(function(story) {
  return getJSON(story.chapterUrls[0]);
}).then(function(chapter1) {
  addHtmlToPage(chapter1.html);
}).catch(function() {
  addTextToPage("Failed to show chapter");
}).then(function() {
  document.querySelector('.spinner').style.display = 'none';
});

ES7 async functions

async function loadStory() {
  try {
    let story = await getJSON('story.json');
    let chapter1 = await getJSON(story.chapterUrls[0]);
    addHtmlToPage(chapter1.html);
  } catch (e) {
    addTextToPage("Failed to show chapter");
  }
  document.querySelector('.spinner').style.display = 'none';
}
async function() {
  await loadStory();
  console.log("Yey, story successfully loaded!");
}

More ES7 features

  • github.com/tc39/ecma262
  • babeljs.io/docs/plugins/
Made with Slides.com