Getting started with GlueStick

GlueStick, our final gift of 2015 to the world, was officially released on Christmas Day!  In case you missed our initial announcement, GlueStick is a command line interface (CLI) that helps you rapidly develop universal React applications. GlueStick handles asset package management, server-side rendering, and comes with a fully-functional testing environment and generators to help you move quickly. For more information on why and how we built GlueStick, see our original post here. Also, check it out on GitHub.

This tutorial will explain GlueStick and some of its awesome features. This tool was designed by TrueCar’s very own Rails team to improve productivity, and perhaps bring a little joy to the process of building front-end web applications.

Currently the test suite runs through Chrome, but we will be adding support for more browsers in the future. So, the assumption, for the remainder of this guide, is that you will be using Chrome.

Installation

We need to install two different components to get started.

We set out to truly build a tool for tomorrow’s web applications, not just for the needs we currently face. So, our primary target, at this time, is the latest version of Node v5+. You should be able to use Node v4+, as well, but we have found Node v5+ to be a more precise and better integrated version. So, install the suitable version of Node, and let’s get started.

To install GlueStick you just need to run the following command to get GlueStick on your machine. sudo npm install gluestick -g

Creating a new application

GlueStick attempts to handle as much of the non-application specific code as it can for you. However, there is still some boilerplate code and a common folder structure that you will need in order to build a GlueStick app. To make this simple, GlueStick provides the “new” command.

Time to create a new project: to create a new project, run the following command:
gluestick new ExampleApp

This is will create a folder named ExampleApp in the folder on which you just ran the command. GlueStick sets up the folder structure and installs all of the node_modules that you will need for the base application.
Here is an example of what you can expect to see in the newly created ExampleApp folder:

assets/
  - css
    - normalize.css
Index.js
package.json
src/
    - actions/
    - components/
        - Home.js
        - MasterLayout.js
    - config/
        - application.js
        - routes.js
    - containers/
        - HomeApp.js
    - reducers/
        - index.js
test/
    - components/
        - Home.test.js

Running your application

Now that you have a new project started, running the application in the browser is next. Change directories into the ExampleApp folder `cd ExampleApp` and run the `start` command.
`gluestick start`

This will kick off a few things. It is going to run your code through webpack dev server as well as run our universal web server. The webpack dev server serves our file assets to the web browser and enables hot module replacement (explained more below). The universal web server is the part that delivers the initial html, generated from front-end code to the web browser, before the file assets are hooked up. The universal web server piece will also let us do server-side rendering of your React application.

Not only does the `start` command kick off our servers, but it also starts up our test runner in its own dedicated Chrome instance so you can have tests continuously running while you work on your app.
Now, open your browser to http://localhost:8888/ and you should be greeted with the text “Home”.

Hot Module Replacement

One of our favorite features of GlueStick is hot module replacement, an awesome feature that allows for updates to show without refreshing the browser. To start, open src/components/Home.js in your favorite editor, and replace the word “Home” with “Hello World!”. Make sure the web browser is visible when you do this. and you will see that “Home” will magically change to “Hello World!” right before your very eyes…without even having to refresh the browser.

You’re going to work on styling next, so let’s discuss our styling tool: Radium. (We won’t go into all of the details as to why Radium is so amazing; the Radium docs do a good enough job of that.) GlueStick projects have Radium in-built for you so all its awesomeness is there for your use.

Follow these four simple steps to use Radium with your component.
1. Import Radium

import Radium from "radium"

2. Decorate your class using the Radium decorator

@Radium
export default class Home extends Component {

3. Define your styles in an object.

const styles = {
    header: {
        color: "green"
    }
};

4. Apply the style to an element

<div style={styles.header}>Hello World!</div>

So your entire component should now look like this:

import React, { Component, PropTypes } from "react";
import Radium from "radium";

@Radium
export default class Home extends Component {
    render () {
        return (
            <div style={styles.header}>
                Hello World!
            </div>
        );
    }
}

const styles = {
    header: {
        color: "green"
    }
};

Immediately after you save the file, “Hello World!” will turn green—no refresh needed. The ability to quickly iterate over styles without refreshing the browser is a huge time saver and results in a boost in productivity. Thanks to the array of open source tools we use under the hood with GlueStick, this kind of flow was relatively easy to build and is very helpful.

Generating a Container

GlueStick includes several generators to help boost productivity even further. Given its role in the application development, you will start with the Container generator. GlueStick apps use Redux for managing application-state. When using Redux, components that are directly connected to Redux are referred to as “containers.” These will generally be components that we connect to a route in our router. Now that the essentials are in place, we’re going to walk you through the creation of an actual application. For this guide, you will build a todo list application. (After all, we ARE trying to boost productivity.) Your first step will be generating a container for your todo list. gluestick generate container Todos This will generate the following file:

src/containers/Todos.js

import React, { Component, PropTypes } from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";

@connect(
    (state) => ({/** _INSERT_STATE_  **/}),
    (dispatch) => bindActionCreators({/** _INSERT_ACTION_CREATORS_ **/}, dispatch)
)
export default class Todos extends Component {
    static fetchData ({dispatch}) {}

    render () {
        return (
            <div>Todos</div>
        );
    }
}

As you can see, this is a simple component that is already hooked up to the Redux store for you. Notice the static method, fetchData, gives you a hook whereby you can asynchronously fetch data before your container is rendered. Your server-side request won’t respond until all of the data is filled in so that you don’t end up returning to your app’s loading state for every thing. To match this container to a route, we will need to add an entry to the routes file. If you have used React Router, then the routes file will look familiar to you, since we built this in to GlueStick already. However, we have already hooked up the router for you so that you don’t have to mess with it and can just focus on defining your routes.

The routes file is located at src/config/routes.js and it looks like this:

import React from "react";
import { Route, IndexRoute } from "react-router";

import MasterLayout from "../components/MasterLayout";
import HomeApp from "../containers/HomeApp";

export default (
    <Route name="app" component={MasterLayout} path="/">
        <IndexRoute name="home" component={HomeApp} />
    </Route>
);

Matching your newly created Todos container with a route is simple. First, import your container, then add a nested route with the correct path and component.

import React from "react";
import { Route, IndexRoute } from "react-router";

import MasterLayout from "../components/MasterLayout";
import HomeApp from "../containers/HomeApp";
import Todos from "../containers/Todos";

export default (
    <Route name="app" component={MasterLayout} path="/">
        <IndexRoute name="home" component={HomeApp} />
        <Route name="todos" component={Todos} path="/todos" />
    </Route>
);

Now direct your browser to http://localhost:8888/todos and your new container should show up.

Generating a Component

React applications are made up of lots of components. They almost all start with the same few lines of code, so we made a generator to speed things up. We want to encourage developers to write unit tests for their components so we didn’t stop there. Whenever you use the generator to create a new component, it will also create a test file for that component, along with a very basic test to verify that it is rendering without any issues. To generate a new component, simply enter: gluestick generate component TodoList

This will generate two files: src/components/TodoList.js

import React, { Component, PropTypes } from "react";

export default class TodoList extends Component {
    render () {
        return (
            <div>
                TodoList
            </div>
        );
    }
}

test/components/TodoList.test.js

import TodoList from "components/TodoList";

describe("components/TodoList", () => {
    it("should render without an issue", () => {
        const subject = <TodoList />;
        const renderedSubject = TestUtils.renderIntoDocument(subject);
        expect(renderedSubject).to.not.equal(undefined);
    });
});

As you continue to develop to your application, our GlueStick test suite will automatically re-run your tests. The results from these tests will show up in your terminal as they are made available. Also, you will receive push notifications each time the tests are run to show you whether or not they passed. In order to ensure as complete a testing environment as possible, our test suite is made up of Karma, Mocha, Sinon, and Chai.

Generating a Reducer

GlueStick applications use Redux to manage application-state. Put simply, Redux lets you manage your application-state as a single object that is propagated throughout your app. To change the state, take your current state object and pass it off to reducers that determine what the new state should look like based on a given action. GlueStick hooks up all that boilerplate code for you so that you can get right down to the business of creating the app. To generate a reducer, enter: gluestick generate reducer todos

This will create a new file src/reducers/todos.js and modify the existing reducers index file src/reducers/index.js. GlueStick looks to src/reducers/index.js to know which reducers our Redux store should incorporate.
src/reducers/todos.js

const INITIAL_STATE = null;

export default (state=INITIAL_STATE, action) => {
    switch (action.type) {
        default:
            return state;
    }
};

src/reducers/index.js

export { default as todos } from "./todos"

Next, you’ll need to modify your reducer to manage an array, so you can add todo list items to the piece of the state of which this reducer is in charge. For now, just change the initial state from null to an array with two items in it ["First Item", "Second Item"];

const INITIAL_STATE = ["First Item", "Second Item"];

export default (state=INITIAL_STATE, action) => {
    switch (action.type) {
        default:
            return state;
    }
};

Hooking up your data

Now that you have a reducer that is responsible for handling the todos part of your application-state, you can expose that state to the rest of your application through the container.
In order to do so, update your todo container to the following:
src/containers/Todos.js

import React, { Component, PropTypes } from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";

import TodoList from "../components/TodoList";

@connect(
    // Expose the part of the state that our todos reducer is reponsible for to our container
    (state) => ({todos: state.todos}),
    (dispatch) => bindActionCreators({/** _INSERT_ACTION_CREATORS_ **/}, dispatch)
)
export default class Todos extends Component {
    static fetchData ({dispatch}) {}

    render () {
        // Use our TodoList component and pass our todos to it
        return (
            <TodoList todos={this.props.todos} />
        );
    }
}

Next, you’ll need to get your TodoList component showing the items on your todo list. Update your TodoList component

import React, { Component, PropTypes } from "react";

export default class TodoList extends Component {
    render () {
        const todos = this.renderTodos();
        return (
            <div>
                {todos}
            </div>
        );
    }

    renderTodos () {
        if (!this.props.todos) return;

        return this.props.todos.map((todo, index) => {
            return <div key={index}>{todo}</div>;
        });
    }
}

The renderTodos method will return an array of elements containing the todo list text. You’ll assign this array to a variable and expose it in your render function.

You will need to refresh the browser for this update to work because our reducer was original set up with an initial state that wasn’t an array. Hot module loading will not replace an existing state. Once you refresh the browser, you should now see both of your todo list items on the page.

Turn off JavaScript

Now…to server-side rendering. Try turning off javascript in your web browser and refreshing the page. You’ll notice that you get the exact same result! That is because we render the same page on the server before it is sent to the user. This makes it so that your React applications can be fully search engine optimized.
Now turn JavaScript back on and refresh the browser so you can continue building your app.

Generate a component for adding todos

Now that you set up the rendering of the todo list items, it is time to allow users to add new items to the list. Generate a new component named AddTodo.
gluestick generate component AddTodo

Once that is created, edit your TodoList component to include it above your list.
src/components/TodoList.js

import React, { Component, PropTypes } from "react";

// Import our AddTodo component
import AddTodo from "./AddTodo";

export default class TodoList extends Component {
    render () {
        const todos = this.renderTodos();
        // Update our render method to include our AddTodo component
        return (
            <div>
                <AddTodo />
                {todos}
            </div>
        );
    }

    renderTodos () {
        if (!this.props.todos) return;

        return this.props.todos.map((todo, index) => {
            return <div key={index}>{todo}</div>;
        });
    }
}

As soon as you save this file, you will see AddTodo show up above your todo list items. You’ll want that to be an input form, so edit the the AddTodo component.
src/components/AddTodo.js

import React, { Component, PropTypes } from "react";

export default class AddTodo extends Component {
    render () {
        return (
            <div>
                <form onSubmit={this.didSubmit}>
                    <input type="text" ref={(input) => this.input = input} />
                </form>
            </div>
        );
    }

    didSubmit = (e) => {
        e.preventDefault();
        const newItem = this.input.value;
        this.input.value = "";
        // @TODO: send our new item to redux
    }
}

Your AddTodo component will render a form that, when submitted, will call your didSubmit method. You will call e.preventDefault() to prevent the form from doing a traditional form submission that would leave the page. You’ll make your input element available to your methods using the ref property. [See React docs for more information on refs] (https://facebook.github.io/react/docs/more-about-refs.html). When didSubmit is called, you will capture the value from the input and then clear the form. The next step will be to update your application-state to include this new todo item. Before you can do that, you need to dive into action creators.

Make an action creator

The reducer that you just created determines the new state based on an action that is passed to it. These actions are typically simple objects that include a type property and value and will include any additional data the reducer will need to perform the action. An action for creating a new todo might look like this:

{
    type: "ADD_TODO",
    text: "Write getting started guide"
}

Given that typing these objects out every time is inefficient, the typical flow in a Redux application uses “action creators.” These are very simple functions that return action objects. This will give us a nice place to expose the type as a constant for your reducer, as well. You don’t create a generator for action creators because, as you will see, there’s nothing to generate.

Create a new file src/actions/todos.js

export const ADD_TODO = "ADD_TODO";

export function addTodo (text) {
    return {
        type: ADD_TODO,
        text: text 
    };
}

Now tell the reducer what to do with this action. Edit src/reducers/todos.js

import { ADD_TODO } from "../actions/todos";

const INITIAL_STATE = ["First Item", "Second Item"];

export default (state=INITIAL_STATE, action) => {
    switch (action.type) {
        case ADD_TODO:
            return [action.text, ...state];
        default:
            return state;
    }
};

First, import your ADD_TODO constant at the top. Then, add a special case to a switch statement for actions of that type. Then, create a new array with the todo text and copy over the old array using the ES2015 spread operator. Note: create a new array; don’t mutate the old array. This is an important requirement of Redux as their documentation states: “For now, just remember that the reducer must be pure. Given the same arguments, it should calculate the next state and return it. No surprises. No side effects. No API calls. No mutations. Just a calculation.” (See the Redux documentation on Reducers for more information.)

Hook up your action creator

Redux sends actions to the reducers through a dispatch method that is provided for you through the @connect decorator that GlueStick automatically sets up for you. Redux gives you the ability to bind your action creators to the dispatcher with its bindActionCreators method. GlueStick also sets most of this up for you so that you only need to pass your action creators in the place where the code says /** _INSERT_ACTION_CREATORS_ **/.

Update your Todos container so that you can pass your bound addTodo action creator to the AddTodo component.
src/containers/Todos.js

import React, { Component, PropTypes } from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";

import TodoList from "../components/TodoList";
// import our addTodo action creator
import { addTodo } from "../actions/todos";

@connect(
    (state) => ({todos: state.todos}),
    // bind our action creator to the dispatch method so we can pass it around
    // without worrying about how to dispatch the action to our Redux store
    (dispatch) => bindActionCreators({addTodo}, dispatch)
)
export default class Todos extends Component {
    static fetchData ({dispatch}) {}

    render () {
        // Pass the bound addTodo action creator to TodoList
        return (
            <TodoList todos={this.props.todos} addTodo={this.props.addTodo} />
        );
    }
}

Now that TodoList has been given the addTodo method, you can continue to pass it to your AddTodo form. Edit the TodoList component like the following:
src/components/TodoList.js

import React, { Component, PropTypes } from "react";
import AddTodo from "./AddTodo";

export default class TodoList extends Component {
    render () {
        const todos = this.renderTodos();
        return (
            <div>
                <AddTodo addTodo={this.props.addTodo} />
                {todos}
            </div>
        );
    }

    renderTodos () {
        if (!this.props.todos) return;

        return this.props.todos.map((todo, index) => {
            return <div key={index}>{todo}</div>;
        });
    }
}

The only line we changed was that we changed <AddToo /> to <AddTodo addTodo={this.props.addTodo} />.

Now, you can finish the AddTodo component by using the addTodo method. Edit the file and replace your // @TODO… comment with this.props.addTodo(newItem);

import React, { Component, PropTypes } from "react";

export default class AddTodo extends Component {
    render () {
        return (
            <div>
                <form onSubmit={this.didSubmit}>
                    <input type="text" ref={(input) => this.input = input} />
                </form>
            </div>
        );
    }

    didSubmit = (e) => {
        e.preventDefault();
        const newItem = this.input.value;
        this.input.value = "";
        this.props.addTodo(newItem);
    }
}

Asynchronous action creators

If you made it this far, you just built a todo list application using GlueStick and the array of frameworks it glues together for you! Hip Hip Hooray! You’ll notice that refreshing the web browser resets the list to the initial state. That is because you haven’t done anything to persist the data yet. You could keep things in the browser by using LocalStorage or hitting a server API in your action creators.

GlueStick gives you a couple middleware functions for your Redux store: Redux Thunk and our own Promise middleware.

Creating an action creator that needs to hit an API should be done with the Promise middleware. Here is an example of what that would look like:

export function getTodos () {
    return {
        type: "GET_TODOS",
        promise: new Promise((resolve) => {
            someAsyncMethod((result) => {
                resolve(result);
            });
        })
    };
}

The promise middleware can fire off three of its own actions. If your action type is GET_TODOS, then it will first dispatch GET_TODOS_INIT. It is up to you if you want to handle this action or not. This is a good place to update the state to show a loading spinner.

GET_TODOS will only be triggerd once the promise resolves. When you call resolve from inside your promise, any value passed to the resolve method will be available on the action object under as action.value.
GET_TODOS_FAILURE will be triggered if the promise fails to resolve. This gives you the chance to notify the user that something went wrong.

Prefetching data with fetchData

Earlier, we mentioned the static fetchData method. React Router allows you to call a method before rendering a component. In this case, all of the containers have the opportunity to let the server and client know if you need to fetch data before showing the component.
To use this method, create an action creator using the Promise middleware and return the result of dispatching that action.
Example:

import React, { Component, PropTypes } from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";

import TodoList from "../components/TodoList";
import { getTodos, addTodo } from "../actions/todos";

@connect(
    (state) => ({todos: state.todos}),
    (dispatch) => bindActionCreators({addTodo}, dispatch)
)
export default class Todos extends Component {
    static fetchData ({dispatch}) {
        // It is important you `return` the result and that you pass the result of
        // `getTodos()`, not the the function itself. Lastly make sure getTodos()
        // returns an action using the promise middleware so the the dispatch method
        // returns a promise.
        return dispatch(getTodos());
    }

    render () {
        return (
            <TodoList todos={this.props.todos} addTodo={this.props.addTodo} />
        );
    }
}