Menu hamburger icon

Improving Your Development Workflow with Webpack, React Hot Loader and Onsen UI

Onsen UI Hot Reloading

One of the great things about React is that there are a lot of tools and libraries available. One of the most popular is the React Hot Reloader by Dan Abramov, who also created Redux. The React Hot Reloader enables developers to make code changes and apply these changes without the application losing its state. In this tutorial we will build a small application step by step to demonstrate the power of Webpack and React and also show how OnsenUI users can benefit from it.

Setting up the Environment

We will start with a blank NodeJS project.

npm init -f

This creates an empty project. If you get an error, make sure you have node and npm installed. A small guide can be found here. After initializing the project it is time to install the dependencies. We will need the following packages:

  • React: React is the library, which we will use for writing our JavaScript applications.
  • Babel: Babel transforms ES2015 and JSX files to JavaScript in such a way that it can be easily executed by any modern browser.
  • Webpack: Webpack is a bundler, that puts all your JavaScript files together into one big file. It also comes with many tools like the Development Server that support module reloading.
  • React Hot Reloader Plugin: This is the plugin for Babel and Webpack that enables hot reloading in React, such that after a file changes, the state of the components will still remain.
  • OnsenUI: OnsenUI is a UI library for mobile hybrid applications that provide many components like Navigators and Tabbar out of the box. It also has a nice autostyling feature.

These plugins can all be installed with one command:

npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-0 react-hot-loader@3.0.0-beta.1 webpack webpack-dev-server onsenui react react-dom react-onsenui

As a side node we use the beta version of React Reloader 3.0, since it is very stable and fixes a lot of issues that the previous version has.

Configuring Webpack

Now that we have installed all the dependencies we need to configure Webpack. Webpack works like this: It first looks for a main JavaScript file and then searches through for all dependencies indicated by require and import statements and puts them all together in one file. To configure this process, Webpack supports custom loaders and plugins.

A loader is a program that transforms a file encountered by Webpack to a JavaScript file that can be processed by Webpack. With the power of loaders Webpack can import json, css and any kind of file type as long as there is a loader for it. For our purposes we will use the babel-loader to transform React applications using JSX to JavaScript.

A plugin is a program that allows altering the inner processes of webpack. An example for this is the HotModuleReplacementPlugin, that allows webpack to replace files during runtime. The list of plugins available can be found here.

Lets create our configuration file now:

var path = require('path');
var webpack = require('webpack');

module.exports = {
  devtool: 'eval',
  entry: [
    'react-hot-loader/patch',
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/only-dev-server',
    './src/index'
  ],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath: '/static/'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],

  devServer: {
    colors: true,
    historyApiFallback: true,
    inline: false,
    port: 3000,
    hot: true
  },

  module: {
    loaders: [{
      test: /\.js$/,
      loader: 'babel',
      query: {
        "presets": ["es2015", "stage-0", "react"],
        "plugins": ["react-hot-loader/babel"]
      },
      include: path.join(__dirname, 'src')
    }]
  }
};

The configuration file in webpack is a JavaScript file that exports a configuration object. We can configure things like the entry JavaScript file of where webpack should start processing the files, the output file, the plugins, the loaders and much more. The most important part is the babel loader that uses the react hot reloading plugin. For further information, check the [website] (https://webpack.github.io/).

After doing this, we need to add a script to start our server: In the package.json, we can replace the script value with:

  "scripts": {
    "start": "webpack-dev-server --progress"
  },

That is all for our Webpack configuration: We can start coding now.

Our first example

This will allow us to start the server with npm run. However, we need to first create a simple entry file. Let us first create a simple html file:

<!doctype html>
<html>
  <head>
    <title>Sample App</title>
    <link rel="stylesheet" type="text/css" href="./node_modules/onsenui/css/onsenui.css">
    <link rel="stylesheet" type="text/css" href="./node_modules/onsenui/css/onsen-css-components.css">
  </head>
  <body>
    <div id='root'>
    </div>
    <script src="/static/bundle.js"></script>
  </body>
</html>

This file includes the OnsenUI CSS styles provided by the node package. and our root entry component.

import { AppContainer } from 'react-hot-loader';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

const rootElement = document.getElementById('root');
ReactDOM.render(
  <AppContainer>
    <App />
  </AppContainer>,
  rootElement
);

if (module.hot) {
  module.hot.accept('./App', () => {
    const NextApp = require('./App').default;
    ReactDOM.render(
      <AppContainer>
         <NextApp />
      </AppContainer>,
      rootElement
    );
  });
}

This might look a little bit overwhelming at first, but its a general pattern that is the same for all applications. All the elements need to be wrapped in the AppContaineer and we write down the logic what should happen if hot reloading happens.

Now we can write our first simple application App.js.

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

export default class App extends React.Component {
  render() {
    return (
      <div>
        React test example.
      </div>
    );
  }
}

This example contains nothing more than a simple text. Now we can start the server and see it in action. First we need to run npm start and open the browser at http://localhost:3000/. We can change for example the text and change the font. Here is a live example:

Demonstration React Hot Reloader

The resulting code looks like this:

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

export default class App extends React.Component {
  render() {
    return (
      <div style={{fontSize: 20}}>
        React test example with hot reloading and font changes.
      </div>
    );
  }
}

All the code for this example can be found in this GitHub repository.

Hot reloading with OnsenUI

These changes are already nice, but the hot reloader is much more powerful. To demonstrate this, let us build a small mobile app using the React Components for OnsenUI.

We will create a simple App with two screen: The first screen will have a simple Input box and output the text written in that input. The second screen will just be some text. We put those screens together using the Navigator.

import 'onsenui'
import {Page, Navigator, Button, Input} from 'react-onsenui'

class PageOne extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  onChangeText(event) {
    this.setState({text: event.target.value});
  }

  pushPage() {
    this.props.navigator.pushPage({
      component: PageTwo
    });
  }

  render() {
    return (
      <Page>
        <div style={{padding: 20}}>
          <Input
            placeholder='Input field'
            style={{height: 20}}
            value={this.state.text}
            onChange={this.onChangeText.bind(this)} /> <br />
          <p>
            You typed {this.state.text}
          </p>
        </div>
      </Page>

    );
  }
};

class PageTwo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    return (
      <Page>
        <div style={{padding: 20}}>
          This is a second Page
        </div>
      </Page>

    );
  }
}

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

  renderPage(route, navigator) {
    const props = route.props || {};
    props.navigator = navigator;

    return React.createElement(route.component, props);
  }

  render() {
    return (
    <Navigator
      initialRoute={{
        component: PageOne
      }}
      renderPage={this.renderPage.bind(this)}
    />
    );
  }
}

To demonstrate hot reloading we will enter some text in the first page. Push the second Page and then add a pop button and save the file:

class PageTwo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    return (
      <Page>
        <div style={{padding: 20}}>
          This is a second Page
          <Button onClick={this.pushPage.bind(this)}> Push page </Button>
        </div>
      </Page>
    );
  }
}

Just with saving the file we will be able to use the pop button and still see the text that we had originally entered, the state of the application stays the same. Here is a live demonstration:

Demonstration React reloader

Again, the source code can be found at a GitHub repository to play with.

Conclusion and reference

Hot Reloading is a very nice feature that can speed up the application development process. In the future, we plan to add the hot reloading to the Monaca CLI. For those who want to learn more, we highly recommend the talk by Dan Abramov, the creator of this plugin and also of Redux. Its also worth checking out the official website. If any questions arise, we will be glad to answer them in our Community.

Comments