React Setup

Part 2, Lesson 5



Let's turn our attention to the client-side and add React...


React is a declarative, component-based, JavaScript library for building user interfaces.

If you're new to React, review the Quick Start and the excellent Why did we build React? blog post. You may also want to step through the Intro to React tutorial to learn more about Babel and Webpack.

Make sure you have Node and NPM installed before continuing:

$ node -v
v7.10.0
$ npm -v
4.2.0

Project Setup

We'll be using the excellent Create React App tool to generate a boilerplate that's all set up and ready to go.

Make sure you understand what's happening beneath the scenes with Webpack and babel. For more, check out the Intro to React tutorial.

Start by installing Create React App globally:

$ npm install [email protected] --global

Navigate to the flask-microservices-client directory and create the boilerplate:

$ create-react-app .

This will also install all dependencies. Once done, start the server:

$ npm start

Now we're ready build our first component!

First Component

First, to simplify the structure, remove the App.css, App.js, App.test.js, and index.css from the "src" folder, and then update index.js:

import React from 'react';
import ReactDOM from 'react-dom';

const App = () => {
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-4">
          <br/>
          <h1>All Users</h1>
          <hr/><br/>
        </div>
      </div>
    </div>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

What's happening?

  1. After importing the React and ReactDom classes, we created a functional component called App, which returns JSX.
  2. We then use the render() method from ReactDOM to mount the App to the DOM into the HTML element with an ID of root.

    Take note of <div id="root"></div> within the index.html file in the "public" folder.

Add Bootstrap to index.html in the head:

<link
  href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
  rel="stylesheet"
>

Class-based Component

Update index.js:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class App extends Component {
  constructor() {
    super()
  }
  render() {
    return (
      <div className="container">
        <div className="row">
          <div className="col-md-4">
            <br/>
            <h1>All Users</h1>
            <hr/><br/>
          </div>
        </div>
      </div>
    )
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

What's happening?

  1. We created a class-based component, which runs automatically when an instance is created (behind the scenes).
  2. When ran, super() calls the constructor of Component, which App extends from.

You may have already noticed, but the output is the exact same as before.

AJAX

To connect the client to the server, add a getUsers() method to the App class:

getUsers() {
  axios.get(`${process.env.REACT_APP_USERS_SERVICE_URL}/users`)
  .then((res) => { console.log(res); })
  .catch((err) => { console.log(err); })
}

We'll use Axios to manage the AJAX call:

$ npm install [email protected] --save

Add the import:

import axios from 'axios';

You should now have:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';

class App extends Component {
  constructor() {
    super()
  }
  getUsers() {
    axios.get(`${process.env.REACT_APP_USERS_SERVICE_URL}/users`)
    .then((res) => { console.log(res); })
    .catch((err) => { console.log(err); })
  }
  render() {
    return (
      <div className="container">
        <div className="row">
          <div className="col-md-4">
            <br/>
            <h1>All Users</h1>
            <hr/><br/>
          </div>
        </div>
      </div>
    )
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

To connect this to Flask, open a new terminal window, navigate to the flask-microservices-users, activate the virtual environment, and set the environment variables:

$ source env/bin/activate
$ export APP_SETTINGS=project.config.DevelopmentConfig
$ export DATABASE_URL=postgres://postgres:[email protected]:5432/users_dev
$ export DATABASE_TEST_URL=postgres://postgres:[email protected]:5432/users_test

You may need to change the username and password depending on your local Postgres config.

With your local Postgres server running, create and seed the local database and run the server:

$ python manage.py recreate_db
$ python manage.py seed_db
$ python manage.py runserver -p 5555

Your server should be listening on http://localhost:5555. Navigate to http://localhost:5555/users in your browser to test.

Turning back to React, we need to add the environment variable process.env.REACT_APP_USERS_SERVICE_URL. Kill the Create React App server, and then run:

$ export REACT_APP_USERS_SERVICE_URL=http://localhost:5555

All custom environment variables must begin with REACT_APP_. For more, check out the official docs.

We still need to call the getUsers() method, which we can do in the constructor():

constructor() {
  super()
  this.getUsers()
}

Run the server - via npm start - and then within Chrome DevTools, open the JavaScript Console. You should see the following error:

XMLHttpRequest cannot load http://localhost:5555/users. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.

In short, we're making cross-origin AJAX request (from http://localhost:3000 to http://localhost:5555), which is a violation of the browsers "same origin policy". Let's use the Flask-CORS extension to handle this.

Within the flask-microservices-users project directory, kill the server and then install Flask-CORS:

(env)$ pip install flask-cors==3.0.2
(env)$ pip freeze > requirements.txt

To keep things simple, let's allow cross origin requests on all routes. Simply update create_app() in flask-microservices-users/project/init.py like so:

def create_app():

    # instantiate the app
    app = Flask(__name__)

    # enable CORS
    CORS(app)

    # set config
    app_settings = os.getenv('APP_SETTINGS')
    app.config.from_object(app_settings)

    # set up extensions
    db.init_app(app)

    # register blueprints
    from project.api.views import users_blueprint
    app.register_blueprint(users_blueprint)

    return app

Add the import:

from flask_cors import CORS

To test, fire back up both servers, open the JavaScript Console again, and this time you should see the results of console.log(res);. Let's parse the JSON object:

getUsers() {
  axios.get(`${process.env.REACT_APP_USERS_SERVICE_URL}/users`)
  .then((res) => { console.log(res.data.data); })
  .catch((err) => { console.log(err); })
}

Now you should have an array with two objects in the JavaScript Console.

Before we move on, we need to do a quick refactor. Remember how we called the getUsers() method in the constructor?

constructor() {
  super()
  this.getUsers()
}

Well, the constructor() fires before the component is mounted to the DOM. What would happen if the AJAX request took longer than expected and the component was mounted before it was complete? This introduces a race condition. Fortunately, React makes it fairly simple to correct this via Lifecycle Methods.

Component Lifecycle Methods

Class-based components have several functions available to them that execute at certain times during the life of the component. These are called Lifecycle Methods. Take a quick look at the official documentation to learn about each method and when each is called.

The AJAX call should happen in the componentDidMount() method:

componentDidMount() {
  this.getUsers();
}

Update the component:

class App extends Component {
  constructor() {
    super()
  }
  componentDidMount() {
    this.getUsers();
  }
  getUsers() {
    axios.get(`${process.env.REACT_APP_USERS_SERVICE_URL}/users`)
    .then((res) => { console.log(res.data.data.users); })
    .catch((err) => { console.log(err); })
  }
  render() {
    return (
      <div className="container">
        <div className="row">
          <div className="col-md-4">
            <br/>
            <h1>All Users</h1>
            <hr/><br/>
          </div>
        </div>
      </div>
    )
  }
}

Make sure everything still works as it did before.

State

To add the state - i.e., the users - to the component we need to use setState(), which is an asynchronous function use to update state.

Update getUsers():

getUsers() {
  axios.get(`${process.env.REACT_APP_USERS_SERVICE_URL}/users`)
  .then((res) => { this.setState({ users: res.data.data.users }); })
  .catch((err) => { console.log(err); })
}

Add state to the constructor:

constructor() {
  super()
  this.state = {
    users: []
  }
}

So, this.state adds the state property to the class and sets users to an empty array.

Review Using State Correctly from the official docs.

Finally, update the render() method to display the data returned from the AJAX call to the end user:

render() {
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-6">
          <br/>
          <h1>All Users</h1>
          <hr/><br/>
          {
            this.state.users.map((user) => {
              return <h4 key={user.id} className="well"><strong>{ user.username }</strong> - <em>{user.created_at}</em></h4>
            })
          }
        </div>
      </div>
    </div>
  )
}

What's happening?

  1. We iterated over the users (from the AJAX request) and created a new H4 element. This is why we needed to set an initial state of an empty array - it prevents map from exploding.
  2. key? - used by React to keep track of each element. Review the official docs for more.

Functional Component

Let's create a new component for the users list. Add a new folder called "components" to "src". Add a new file to that folder called UsersList.jsx:

import React from 'react';

const UsersList = (props) => {
  return (
    <div>
      {
        props.users.map((user) => {
          return <h4 key={user.id} className="well"><strong>{user.username }</strong> - <em>{user.created_at}</em></h4>
        })
      }
    </div>
  )
}

export default UsersList;

Again, why did we use a functional component rather than a class-based component?

Notice how we used props instead of state in this component. Essentially, you can pass state to a component with either props or state:

  1. Props - data flows down via props (from state to props), read only
  2. State - data is tied to a component, read and write

    For more, check out ReactJS: Props vs. State

It's a good practice to limit the number of class-based (stateful) components since they can manipulate state, since they are less predictable. If you just need to render data (like in the above case), then use a functional (state-less) component.

Now we need to pass state from the parent to the child component via props. First, add the import to index.js:

import UsersList from './components/UsersList';

Then, update the render() method:

render() {
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-6">
          <br/>
          <h1>All Users</h1>
          <hr/><br/>
            <UsersList users={this.state.users}/>
        </div>
      </div>
    </div>
  )
}

Review the code in each component and add comments as necessary. Commit your code.


React Setup

Let's turn our attention to the client-side and add React...


React is a declarative, component-based, JavaScript library for building user interfaces.

If you're new to React, review the Quick Start and the excellent Why did we build React? blog post. You may also want to step through the Intro to React tutorial to learn more about Babel and Webpack.

Make sure you have Node and NPM installed before continuing:

$ node -v
v7.10.0
$ npm -v
4.2.0

Project Setup

We'll be using the excellent Create React App tool to generate a boilerplate that's all set up and ready to go.

Make sure you understand what's happening beneath the scenes with Webpack and babel. For more, check out the Intro to React tutorial.

Start by installing Create React App globally:

$ npm install [email protected] --global

Navigate to the flask-microservices-client directory and create the boilerplate:

$ create-react-app .

This will also install all dependencies. Once done, start the server:

$ npm start

Now we're ready build our first component!

First Component

First, to simplify the structure, remove the App.css, App.js, App.test.js, and index.css from the "src" folder, and then update index.js:

import React from 'react';
import ReactDOM from 'react-dom';

const App = () => {
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-4">
          <br/>
          <h1>All Users</h1>
          <hr/><br/>
        </div>
      </div>
    </div>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

What's happening?

  1. After importing the React and ReactDom classes, we created a functional component called App, which returns JSX.
  2. We then use the render() method from ReactDOM to mount the App to the DOM into the HTML element with an ID of root.

    Take note of <div id="root"></div> within the index.html file in the "public" folder.

Add Bootstrap to index.html in the head:

<link
  href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
  rel="stylesheet"
>

Class-based Component

Update index.js:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class App extends Component {
  constructor() {
    super()
  }
  render() {
    return (
      <div className="container">
        <div className="row">
          <div className="col-md-4">
            <br/>
            <h1>All Users</h1>
            <hr/><br/>
          </div>
        </div>
      </div>
    )
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

What's happening?

  1. We created a class-based component, which runs automatically when an instance is created (behind the scenes).
  2. When ran, super() calls the constructor of Component, which App extends from.

You may have already noticed, but the output is the exact same as before.

AJAX

To connect the client to the server, add a getUsers() method to the App class:

getUsers() {
  axios.get(`${process.env.REACT_APP_USERS_SERVICE_URL}/users`)
  .then((res) => { console.log(res); })
  .catch((err) => { console.log(err); })
}

We'll use Axios to manage the AJAX call:

$ npm install [email protected] --save

Add the import:

import axios from 'axios';

You should now have:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';

class App extends Component {
  constructor() {
    super()
  }
  getUsers() {
    axios.get(`${process.env.REACT_APP_USERS_SERVICE_URL}/users`)
    .then((res) => { console.log(res); })
    .catch((err) => { console.log(err); })
  }
  render() {
    return (
      <div className="container">
        <div className="row">
          <div className="col-md-4">
            <br/>
            <h1>All Users</h1>
            <hr/><br/>
          </div>
        </div>
      </div>
    )
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

To connect this to Flask, open a new terminal window, navigate to the flask-microservices-users, activate the virtual environment, and set the environment variables:

$ source env/bin/activate
$ export APP_SETTINGS=project.config.DevelopmentConfig
$ export DATABASE_URL=postgres://postgres:[email protected]:5432/users_dev
$ export DATABASE_TEST_URL=postgres://postgres:[email protected]:5432/users_test

You may need to change the username and password depending on your local Postgres config.

With your local Postgres server running, create and seed the local database and run the server:

$ python manage.py recreate_db
$ python manage.py seed_db
$ python manage.py runserver -p 5555

Your server should be listening on http://localhost:5555. Navigate to http://localhost:5555/users in your browser to test.

Turning back to React, we need to add the environment variable process.env.REACT_APP_USERS_SERVICE_URL. Kill the Create React App server, and then run:

$ export REACT_APP_USERS_SERVICE_URL=http://localhost:5555

All custom environment variables must begin with REACT_APP_. For more, check out the official docs.

We still need to call the getUsers() method, which we can do in the constructor():

constructor() {
  super()
  this.getUsers()
}

Run the server - via npm start - and then within Chrome DevTools, open the JavaScript Console. You should see the following error:

XMLHttpRequest cannot load http://localhost:5555/users. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.

In short, we're making cross-origin AJAX request (from http://localhost:3000 to http://localhost:5555), which is a violation of the browsers "same origin policy". Let's use the Flask-CORS extension to handle this.

Within the flask-microservices-users project directory, kill the server and then install Flask-CORS:

(env)$ pip install flask-cors==3.0.2
(env)$ pip freeze > requirements.txt

To keep things simple, let's allow cross origin requests on all routes. Simply update create_app() in flask-microservices-users/project/init.py like so:

def create_app():

    # instantiate the app
    app = Flask(__name__)

    # enable CORS
    CORS(app)

    # set config
    app_settings = os.getenv('APP_SETTINGS')
    app.config.from_object(app_settings)

    # set up extensions
    db.init_app(app)

    # register blueprints
    from project.api.views import users_blueprint
    app.register_blueprint(users_blueprint)

    return app

Add the import:

from flask_cors import CORS

To test, fire back up both servers, open the JavaScript Console again, and this time you should see the results of console.log(res);. Let's parse the JSON object:

getUsers() {
  axios.get(`${process.env.REACT_APP_USERS_SERVICE_URL}/users`)
  .then((res) => { console.log(res.data.data); })
  .catch((err) => { console.log(err); })
}

Now you should have an array with two objects in the JavaScript Console.

Before we move on, we need to do a quick refactor. Remember how we called the getUsers() method in the constructor?

constructor() {
  super()
  this.getUsers()
}

Well, the constructor() fires before the component is mounted to the DOM. What would happen if the AJAX request took longer than expected and the component was mounted before it was complete? This introduces a race condition. Fortunately, React makes it fairly simple to correct this via Lifecycle Methods.

Component Lifecycle Methods

Class-based components have several functions available to them that execute at certain times during the life of the component. These are called Lifecycle Methods. Take a quick look at the official documentation to learn about each method and when each is called.

The AJAX call should happen in the componentDidMount() method:

componentDidMount() {
  this.getUsers();
}

Update the component:

class App extends Component {
  constructor() {
    super()
  }
  componentDidMount() {
    this.getUsers();
  }
  getUsers() {
    axios.get(`${process.env.REACT_APP_USERS_SERVICE_URL}/users`)
    .then((res) => { console.log(res.data.data.users); })
    .catch((err) => { console.log(err); })
  }
  render() {
    return (
      <div className="container">
        <div className="row">
          <div className="col-md-4">
            <br/>
            <h1>All Users</h1>
            <hr/><br/>
          </div>
        </div>
      </div>
    )
  }
}

Make sure everything still works as it did before.

State

To add the state - i.e., the users - to the component we need to use setState(), which is an asynchronous function use to update state.

Update getUsers():

getUsers() {
  axios.get(`${process.env.REACT_APP_USERS_SERVICE_URL}/users`)
  .then((res) => { this.setState({ users: res.data.data.users }); })
  .catch((err) => { console.log(err); })
}

Add state to the constructor:

constructor() {
  super()
  this.state = {
    users: []
  }
}

So, this.state adds the state property to the class and sets users to an empty array.

Review Using State Correctly from the official docs.

Finally, update the render() method to display the data returned from the AJAX call to the end user:

render() {
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-6">
          <br/>
          <h1>All Users</h1>
          <hr/><br/>
          {
            this.state.users.map((user) => {
              return <h4 key={user.id} className="well"><strong>{ user.username }</strong> - <em>{user.created_at}</em></h4>
            })
          }
        </div>
      </div>
    </div>
  )
}

What's happening?

  1. We iterated over the users (from the AJAX request) and created a new H4 element. This is why we needed to set an initial state of an empty array - it prevents map from exploding.
  2. key? - used by React to keep track of each element. Review the official docs for more.

Functional Component

Let's create a new component for the users list. Add a new folder called "components" to "src". Add a new file to that folder called UsersList.jsx:

import React from 'react';

const UsersList = (props) => {
  return (
    <div>
      {
        props.users.map((user) => {
          return <h4 key={user.id} className="well"><strong>{user.username }</strong> - <em>{user.created_at}</em></h4>
        })
      }
    </div>
  )
}

export default UsersList;

Again, why did we use a functional component rather than a class-based component?

Notice how we used props instead of state in this component. Essentially, you can pass state to a component with either props or state:

  1. Props - data flows down via props (from state to props), read only
  2. State - data is tied to a component, read and write

    For more, check out ReactJS: Props vs. State

It's a good practice to limit the number of class-based (stateful) components since they can manipulate state, since they are less predictable. If you just need to render data (like in the above case), then use a functional (state-less) component.

Now we need to pass state from the parent to the child component via props. First, add the import to index.js:

import UsersList from './components/UsersList';

Then, update the render() method:

render() {
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-6">
          <br/>
          <h1>All Users</h1>
          <hr/><br/>
            <UsersList users={this.state.users}/>
        </div>
      </div>
    </div>
  )
}

Review the code in each component and add comments as necessary. Commit your code.