Fullstack React - Chapter 7 Notes

Summary

This post contains notes from chapter 7 in Fullstack React - "Using Webpack with create-react-app". The purpose of this chapter is to learn how to use Webpack and the project template that is created with create-react-app when building an application with React.

Overview 

Up to this point in the book, we loaded React, Babel, and other JavaScript components with script tags in index.html. There are limitations with this approach, most notably of which is the lack of support for JavaScript modules. To get around this limitation, we can use Webpack to bundle our JavaScript and transpile it so it is browser friendly.

Webpack is a JavaScript bundler. JavaScript bundlers provide several benefits including:
  • Helping you to organize your apps
  • Helping you to distribute your app
  • Assisting you with changes during development
  • Providing tools for generating production optimized builds.
create-react-app project
Getting started with Webpack can be overwhelming, especially for beginners, but even for an expert, there are still a lot of boilerplate steps that you must go through to get a project up and running. The React team decided to provide a base project that we can use as a foundation when setting up a new React application. This is similar to selecting a new C# project template, such as a web application, in Visual Studio. The startup project that the React team created is called "create-react-app" and you can use it by running the following command in a terminal window:

$ create-react-app some-app-name

Before you run that command however, you have to install create-react-app:

$ npm i g create-react-app@0.5.0    (note, the @0.5.0 means to use that version, which is the version they used in the book)

npm packages and packages.config
Webpack gives us the ability to use npm packages in the browser. We define the packages that we want to use in the package.json file. Since we are defining the packages we want to use in this file, we no longer have to define the packages we want to use in script tags in index.html

packages.config
The packages.config that is created by create-react-app will look similar to the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
  "name": "heart-webpack",
  "version": "0.1.0",
  "private": true,
  "devDependencies": {
    "react-scripts": "0.6.1"
  },
  "dependencies": {
    "react": "^15.5.4",
    "react-dom": "^15.5.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

There are several properties in packages.config.
  • name - the name of the application (I think this has to be lowercase)
  • version - the current version number of the application
  • private - not sure, what this means
  • devDependencies - These are the dependencies that are installed by npm in a dev environment. A dev environment is identified by the value of the NODE_ENV environment variable.
    • react-scripts - is created by Webpack and it points to all of the dependencies that are needed (i.e. Babel, Webpack). It is the engine that will make everything work.
  • dependencies - These are the dependencies that are installed by npm for a production environment
    • react
    • react-dom
  • scripts - these are commands that we can run against react scripts
    • start - Boots the Webpack development HTTP server. This server will handle requests from our web browser.
      • npm start
    • build - For use in production, this command creates an optimized, static bundle of all our assets.
      • npm run build
    • test - Executes the app's test suite, if present.
      • This is covered in the next chapter
    • eject - Moves the innards of react-scripts into your project's directory. This enables you to abandon the configuration that react-scripts provides, so you can tweak the configuration to your liking.
      • npm run eject

Next the book covers what was created in the src folder. The src folder is where we will write our application.
  • App.css
  • App.js - the core component of the application
  • App.test.js - the test for the core component of the application
  • index.css
  • index.js - the core javascript that references and renders the App component defined in App.js
  • logo.svg
ES6 Modules
ES6 introduced the concept of modules, which are similar to classes in C# (but not exactly the same). Basically it is a way of breaking up related code into separate files that can then be imported and used by other files.

To make code available from outside of the module, it must be exported. There are a number of ways to export code from a module. I liked the following and will likely stick with this in my projects:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
module.exports = {
    function1 : () => {
        return "Function1 says hi" + " and " + function3();
    },

    function2 : () => {
        return "Function2 says hi";
    }
}

const function3 = () => {
    return "Function3 is not available from outside of the module";
}

In the example above, function1 and function2 will be available to external modules. function3 can only be used by code within this module.

To use exported functions, you have to import that module. There are 2 ways to do this:

First, and the way that I will probably most often use, is I can import the entire module:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import MyModule from './MyModule.js'; // I am importing all exports from the module

class App extends Component {
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
          <br />
          {MyModule.function1()}  {/* Calling an exported function from MyModule */}
        </p>
      </div>
    );
  }
}

export default App;

Second, I can import individual functions from those modules and call them directly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { function1 } from './MyModule.js'; // I am importing only function1 from the module

class App extends Component {
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
          <br />
          {function1()}  {/* Calling an exported function1 directly from MyModule */}
        </p>
      </div>
    );
  }
}

export default App;

Note: ES6 knows I am importing a local module because of the relative path './MyModule.js'. Without the './', ES6 will think I am referencing an npm module.

bundle.js
  • bundle.js is a file that is generated by Webpack and it contains all of the JavaScript that we wrote in the src directory as well as all of the JavaScript from any npm packages.
  • A script tag link is added automatically to index.html by Webpack that points to bundle.js and that is how our application knows how to load the core index.js and App.js code.
  • bundle.js is not actually written to disk. It is stored in memory by Webpack and served up when index.html is requested.
  • Webpack converts "everything" into a module including the JavaScript that we write, images, css classes, and npm packages. Everything is considered a module to Webpack and as such, it is all included in the bundle.js file.
Hot-reloading vs Auto-reloading
Webpack supports hot-reloading of css files. What this means is that when we make a change to a css file and save it, the css will be applied to the running application automatically without the need for a page refresh.

Auto-reloading occurs when we make a change to the JavaScript. The page will automatically refresh in the browser window.

Creating a production build
To create a production build, run the following command:

$ npm run build

This will generate a minified index.html file, along with minified and uglified JavaScript, CSS, and images and place them in a folder called build. Because we are minified and uglified these files, Webpack will also generate a .map file, which can be used to map errors that occur in production back to the line in the file in dev.

Creating a project with a server and client side
In order to have a server and a client side, we need to setup 2 projects. In the book, they put the client side in a folder called client. It is within this client folder that we should create the react app (using create-react-app). 

Because we have 2 different projects, we need to run npm install in both the root application directory (where the server code lives) and the client folder (where the client side code lives).

Also, because we have 2 different projects, in order to run them, we need to issue a command for each project. We can do this in 2 separate tabs in the terminal window, or we can use Concurrently, which is a plugin that we can use to issue 2 commands at the same time. In this case, the 2 commands that we want to run are the 2 commands that will start our client and server applications.

The sample code in the food-lookup application setup some JavaScript files that contain commands to start each application. With these files, we can then add a start command to packages.json that will call the commands in those 2 files that will start the client and server applications using just one npm start command. Without this, we would have to execute 2 separate npm run commands.

Cross-Origin Resource Sharing (CORS)
Now that we have 2 applications running on 2 different ports, browsers will typically throw a CORS error when the client application makes a request to the server application. In order to get around this in a development environment, we can either set our request mode to 'no-cors' or we can use Webpack's proxy feature to send all requests through our client application so they are then proxied to the server application. To do this, we need to do the following:

  • Change all urls to point to a relative address (so they go to the same server and port as our client app)
  • Update the Client application's packages.json file to have the property:
    "proxy" : "http://localhost:3001" (i.e. the server and port of the server application)
  • The proxy property is special to create-react-app and instructs it to setup our Webpack development server to proxy API requests to localhost:3001.
  • I understand how this is useful in a development environment, but what do we need to do in a production environment?
Summary
Webpack is a very useful tool that provides a number of benefits:
  • Optimizing - bundling, minification, uglifying
  • Code splitting - configure Webpack so only the files that are needed for a particular request are served up.
  • Tooling - hot and auto-loading, linting, generating production builds
In general, I think I will almost always use create-react-app when I am starting a new React project.

No comments:

Post a Comment

} else { }