React class, ES7 attribute initialization (Part II)

Keywords: Javascript React npm Attribute JSON

This is the second in a series of articles using React in conjunction with ECMAScript6.

Below are links to all the articles in this series:

Github source code for this article

React JS

stay Chapter 1 In this article, we begin by introducing how to use ES6 to create static builds and output Hello from ES6. Not so exciting:)

In this article, we will create a more complex build called CartItem. It will output some product information in the shopping cart, including pictures, titles and prices.

In addition, users can interact with CartItem to increase or decrease the number of items by clicking. And our organization will make dynamic changes to the total price after the interaction.

The final project effect map:

Create index.html file

Let's create a simple html template file.

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>React and ES6 Part 2</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.2/css/foundation.min.css">
</head>
<body>
<div class="root"></div>
<script src="dist/bundle.js"></script>
</body>
</html>

Note that we have added it through cdn Foundation CSS Framework services. It can make our microapplications look beautiful. At the same time, the div with class root will be where we should load it.

Gulpfile.js

Create gulpfile.js file and copy the following code to gulpfile.js file.

var gulp = require('gulp');
var browserify = require('browserify');
var babelify = require('babelify');
var source = require('vinyl-source-stream');

gulp.task('build', function () {
    return browserify({entries: './app.jsx', extensions: ['.jsx'], debug: true})
        .transform('babelify', {presets: ['es2015', 'react']})
        .bundle()
        .on('error', function(err) { console.error(err); this.emit('end'); })
        .pipe(source('bundle.js'))
        .pipe(gulp.dest('dist'));
});

gulp.task('watch', ['build'], function () {
    gulp.watch('*.jsx', ['build']);
});

gulp.task('default', ['watch']);

package.json

  1. Create an empty folder, switch to it, and enter npm init at the terminal to initialize your package.json.

  2. Run NPM install -- save react-dom, which installs react and react-dom into your node_modules folder and saves them as dependency libraries to the package.json file.

  3. Run npm install --save-dev gulp browserify babelify vinyl-source-stream babel-preset-es2015 babel-preset-react, which will install more dependencies on your node_modules folder.

Main application React Component

Create app.jsx:

import React from 'react';
import ReactDOM from 'react-dom';
import CartItem from './cartItem';

const order = {
    title: 'Fresh fruits package',
    image: 'http://images.all-free-download.com/images/graphiclarge/citrus_fruit_184416.jpg',
    initialQty: 3,
    price: 8
};

ReactDOM.render(
    <CartItem title={order.title}
              image={order.image}
              initialQty={order.initialQty}
              price={order.price}/>,
    document.querySelector('.root')
);

What does the above code do?

  • Lines 1-2. We import the React / ReactDOM library.

  • Line 3. Import the CartItem build we are going to create.

  • Lines 5-10. Set relevant properties (including item title, image, initial quantity and price) for the CartItem build.

  • Lines 12-18. Load CartItem to form a DOM element with a CSS class root.

CartItem React Component Architecture

Now is the time to create components responsible for displaying project data and interacting with users.

Add the following code to the cartItem.jsx file:

import React from 'react';

export default class CartItem extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            qty: props.initialQty,
            total: 0
        };
    }
    componentWillMount() {
        this.recalculateTotal();
    }
    increaseQty() {
        this.setState({qty: this.state.qty + 1}, this.recalculateTotal);
    }
    decreaseQty() {
        let newQty = this.state.qty > 0 ? this.state.qty - 1 : 0;
        this.setState({qty: newQty}, this.recalculateTotal);
    }
    recalculateTotal() {
        this.setState({total: this.state.qty * this.props.price});
    }
}

Code Interpretation:

  • Lines 4-10. This is the constructor of the React class in ES6. super(props) is a code that deals with the props of the parent class first, which is essential. The following set of state machine variables is equivalent to the initialization of getInitialState() method state machine variables in ES5. We use this.state to set the initial value of state machine variables. Personally, I prefer to write constructors in ES6.

  • Lines 11-13. CompoonentWillMount () is a method in the life cycle in which we calculate the total price by recalculateTotal().

  • Lines 14-20. Provides users with ways to increase and decrease interaction. These two methods are invoked when the user clicks on the corresponding button (as shown in the previous effect diagram).

CartItem render method

Add a new method to the CartItem class:

export default class CartItem extends React.Component {

    // previous code we wrote here

    render() {
        return (
          <article className="row large-4">
              <figure className="text-center">
                  <p>
                      <img src={this.props.image}/>
                  </p>
                  <figcaption>
                      <h2>{this.props.title}</h2>
                  </figcaption>
              </figure>
              <p className="large-4 column"><strong>Quantity: {this.state.qty}</strong></p>

              <p className="large-4 column">
                  <button onClick={this.increaseQty.bind(this)} className="button success">+</button>
                  <button onClick={this.decreaseQty.bind(this)} className="button alert">-</button>
              </p>

              <p className="large-4 column"><strong>Price per item:</strong> ${this.props.price}</p>

              <h3 className="large-12 column text-center">
                  Total: ${this.state.total}
              </h3>

          </article>
        )
    }
}

Here we just use JSX + to build, plus some basic CSS output beautiful labels.

Don't worry about the use of the {this.increaseQty.bind(this)} code. We'll explain it in detail in the next summary. Now you need to trust me that this code will call the increaseQty() method in the CartItem class.

So now we have beautiful applications that interact with users, which show us how to use ECMAScript6 to write React builds. Personally, I like the new grammar that ES6 brings.

We haven't finished it yet. Before completing this article, we'll look at some of the other new features in ES6.

Default Props and Prop Types for ES6 React classes

Imagine that we want to add some validation and default values to the CartItem build.

Fortunately, you just need to add the following code to the CartItem class.

static get defaultProps() {
    return {
      title: 'Undefined Product',
      price: 100,
      initialQty: 0
    }
}


static propTypes = {
  title: React.PropTypes.string.isRequired,
  price: React.PropTypes.number.isRequired,
  initialQty: React.PropTypes.number
}

PS: You can also delete the above code and add the following code inside the non-CartItem class in cartItem:

CartItem.defaultProps = {
    title: 'Undefined Product',
    price: 100,
    initialQty: 0
}

CartItem.propTypes = {
    title: React.PropTypes.string.isRequired,
    price: React.PropTypes.number.isRequired,
    initialQty: React.PropTypes.number
}

Personally, I prefer the first way of writing, which does not destroy the encapsulation of classes.

After adding the above code, if you add a numeric type value to the title attribute, a warning will appear in the console, which is the function of React property validation.

Bringing ES7 into the project

You may ask a reasonable question, why ES6 has not been finalized and why ES7 appears in your title?

I'll tell you, let's look to the future. We started using new features of non-breaking, property initializers, and decorators.

Even though ES7 is still at a very early stage, some of the proposed new features have been implemented in Babel. These experimental new features are an amazing shift from ES5 to ES7.

First, install the missing npm module through the npm install --save-dev babel-preset-stage-0 command.

Secondly, in order to be able to use the new syntax in our project, we need to make some changes in line 8 of the gulpfile.js file. The code is as follows:

.transform('babelify', {presets: ['react', 'es2015', 'stage-0']})

You can go from GitHub repository Copy the complete code of gulpfile.js directly.

ES7 React Build Attribute Initialization/Default Value/Type

Inside class add this right above :

In CartItem, the following code will be used:

static get defaultProps() {
    return {
      title: 'Undefined Product',
      price: 100,
      initialQty: 0
    }
}


static propTypes = {
  title: React.PropTypes.string.isRequired,
  price: React.PropTypes.number.isRequired,
  initialQty: React.PropTypes.number
}

Replace with the following code:

  static propTypes = {
        title: React.PropTypes.string.isRequired,
        price: React.PropTypes.number.isRequired,
        initialQty: React.PropTypes.number
    };    
    static defaultProps = {
        title: 'Undefined Product',
        price: 100,
        initialQty: 0
    };

ES7 Property Initialiazers for initial state of React component

The last step is to change the initial state from constructor to attribute initialization.

Add the correct code the day after the CartItem constructor:

export default class CartItem extends React.Component {

    // .. constructor starts here
    state = {
        qty: this.props.initialQty,
        total: 0
    };
    // .. some code here

You need to delete the state initialization code from the constructor.

Finally, you need to check the complete code in the cartItem.jsx file:

import React from 'react';

export default class CartItem extends React.Component {
    constructor(props) {
        super(props);
    }

    state = {
        qty: this.props.initialQty,
        total: 0
    };

    static propTypes = {
        title: React.PropTypes.string.isRequired,
        price: React.PropTypes.number.isRequired,
        initialQty: React.PropTypes.number
    };
    static defaultProps = {
        title: 'Undefined Product',
        price: 100,
        initialQty: 0
    };

    componentWillMount() {
        this.recalculateTotal();
    }
    increaseQty() {
        this.setState({qty: this.state.qty + 1}, this.recalculateTotal);
    }
    decreaseQty() {
        let newQty = this.state.qty > 0 ? this.state.qty - 1 : 0;
        this.setState({qty: newQty}, this.recalculateTotal);
    }
    recalculateTotal() {
        this.setState({total: this.state.qty * this.props.price});
    }

    render() {
        return (
          <article className="row large-4">
              <figure className="text-center">
                  <p>
                      <img src={this.props.image}/>
                  </p>
                  <figcaption>
                      <h2>{this.props.title}</h2>
                  </figcaption>
              </figure>
              <p className="large-4 column"><strong>Quantity: {this.state.qty}</strong></p>

              <p className="large-4 column">
                  <button onClick={this.increaseQty.bind(this)} className="button success">+</button>
                  <button onClick={this.decreaseQty.bind(this)} className="button alert">-</button>
              </p>

              <p className="large-4 column"><strong>Price per item:</strong> ${this.props.price}</p>

              <h3 className="large-12 column text-center">
                  Total: ${this.state.total}
              </h3>

          </article>
        )
    }
}

conclusion

In this section, we have mastered the initialization and type binding of ES6 and ES7 React build attributes.

In the next summary, we continue to study the React+ES6 series of tutorials.

Reference Documents

Sweeping application to join the whole stack tribe

Posted by nmcglennon on Thu, 13 Jun 2019 14:00:41 -0700