React, Webpack, and Horizon Quick Start

Mick Berber
Jun 2, 2016 · 9 min read

This tutorial will give you a quick start example to follow along to, and help you build a barebones React chat application with Horizon.io.

source code for this can be found at: https://github.com/mickberber/reChat under the ‘tutorial’ branch

Recently, Speaking with a friend of mine about Angular 2, he was amazed at the command line tool it provides, like the ember cli, generates a ready to go application, test files, server and the whole kit and kaboodle(…somebody check my spelling on that). This is a wonderful development for the JS world. A quick script puts together your application and your ready to go write front end code basically right away! A little while later I heard about Horizon built by the guys behind RethinkDB and how it has a similar quick start command line feature. Horizon is a real-time server for web apps, sitting on top of Rethink. Having built a couple of real-time applications recently my ears perked up straight away. Rethink is meant for applications that leverage web sockets to transfer data quickly and provide an enjoyable real time experience for users while being easier for engineers to set up. It requires installing Rethink(can be done here), an open source database meant for real time web applications(like chat). I got a little interested in these tools and decided to give it a go!

First things first, lets go Horizioning.

npm install -g horizon

This downloads the horizon CLI and the next command will generate a directory and some files for our application.

##initialize applicationhz init [app name]##cd into directorycd [app name]##initialize package.json (hit enter through all options, they don't need special values)npm init## OPTIONAL init git if desiredgit init
hz init will generate these files in the output

For this case we will use the name Rechat! React, Rethinkdb, Rechat. Makes the sense to me. For this tutorial we don’t need to touch the .hz/config.toml file, as Horizon sets up our server connection to the database for us and listens on localhost://8181, but we can take a quick look to see what it is actually doing.(More on toml here)

We can see here the local host connection being set up
As well as the port to the rethinkdb connection

In the Horizon documentation, it tells us that everything in our dist file will be served to the browser on GET requests. This will give us our landing spot for our bundle file that Webpack provides.

Extra reference on the Horizon command line tool:

Webpack it up

Here, we’ll install all of our necessary packages from npm to our package.json, and create our React files. Now, I will caution. This is ALOT of packages to install at once, but thats okay, most of them are for Babel to process our ES6 into ES5. This might feel a little bloated, but recently https://babeljs.io/ released Babel 6.0.0, which changed the process of setting up Webpack. If you experience any issues with an old version of Babel breaking this set up, I suggest reading about fixing that here.

npm install --save react react-dom webpack babel-core babel-polyfill babel-loader babel-plugin-transform-runtime babel-preset-es2015 babel-preset-stage-0 babel-preset-reactmkdir src/components
touch src/index.jsx
touch src/components/app.jsx

This will be our connection from React, to our DOM node, this is accomplished with the ReactDOM.render method. This method will take in a React component, and query the DOM for a tag with the class of ‘attach’ on it:

index.jsx
app.jsx

We’ll also need to alter our existing dist/index.html file by replacing the <marquee> tag with a div that has a class=’attach’ on it, and adding our bundle script:

dist/index.html

Now everything is set up to get our components bundled and attached to the DOM. Webpack will be our build system to accomplish this. We’re gonna need to make a couple more files:

touch babel.rc
touch webpack.config.js

The babel.rc file will help configure how your Webpack transforms our .jsx and ES6. I could go into way more depth here, but this does it way better than I ever could.

babel.rc should contain:

{
“presets”: [“es2015”, “react”, “stage-0”]
}
webpack.config.js

The important parts of this file can be broken down into three parts. The ‘entry’, ‘output’ and the module.loaders/resolve sections.

  • The entry is the file which Webpack will begin bundling the modules that contain our components.
  • The output is where our bundle file will be created.(it can be called anything, but bundle is used as a best practice)
  • Module.loaders/resolve is where Babel does it’s work. Resolve is for the file extensions to identify and change. The loaders are specifying how Babel will transform everything.

At this point, we can run the Horizon dev server:

hz serve --dev

And run Webpack:

webpack --watch --progress --colors

Note: I usually have 3 tabs in my terminal, 2 running the above processes and another for every other command I may need to run.

Extra Reference for Webpack:

At this point, we can navigate to http://localhost:8181 and see this:

Pretty quick huh?

THIS MIGHT BE A GOOD TIME TO TAKE A BREAK, AND GIT COMMIT

Step 2: Getting Reactive

Time to start expanding on what we’ve got going on. Replacing our text from earlier. Were going to add in Message and Messages Components, along with a stylesheet to make it a little more tolerable to look at (right now, we only really need a ‘center’ class). Components are going to be what we do most of our work with in React. If you’re new to React, I recommend getting used to this structure:

import React, { Component } from 'react';class App extends Component {
render() {
//return html
}
}
export default App;

And checking out this.

Let’s make a few more files:

touch src/components/messages.jsx
touch src/components/message.jsx
touch dist/style.css

For now dist/style.css can look like this:

.center {
margin: auto;
border: 1px solid;
border-radius: 2px;
padding: 10px;
}

The Messages component is going to take an array of dummy data objects, map over them and return Message components, passing properties through React for the Message component to deal with and render. Mapping the users’ conversation in this way will allow us to keep our code to a minimum. We only need to write it once, and it will be predictable every time. Leaving the parent component to dealt with logic helps the child component render ‘pure’.

src/components/messages.jsx

import React, { Component } from ‘react’;
import Message from ‘./message’;
class Messages extends Component {
constructor(props) {
super(props);
this.state = {
convo: [{text: ‘this is text’, author: '@steedhelix'},
{text: ‘this is some text’, author: '@steedhelix'},
{text: ‘this is more text’, author: '@steedhelix'},
{text: ‘this is other text’, author: '@steedhelix'}]
};
}
render() {
let msgsjsx = this.state.convo.map(function(message, i){
return <Message msg={message} key={i} />
});
return (<div className='container-fluid'> {msgsjsx} </div> );
}
}
export default Messages;

src/components/message.jsx

import React, { Component } from ‘react’;export default class Message extends Component {
constructor(props) {
super(props);
this.props = props;
}
render() {
return (<div className=’row’>
<div className=’col-xs-2 center’>{this.props.msg.author}</div>
<div className=’col-xs-10 center’>{this.props.msg.text}</div>
</div>);
}
}

Lastly in this step, we want to adjust our app.jsx:

import React, { Component } from ‘react’;
import Messages from ‘./messages’;
class App extends Component {render() {
return (
<div>
<form>
<div className=’center’>
<button>Send Message</button>
<input placeholder=’By’></input>
<input placeholder=’write message here’></input>
</div>
</form>
<Messages />
</div>
);
}
}
export default App;

Make sure to include your stylesheet (and I added bootstrap) in your dist/index.html and rerender your browser:

starting to look like something huh?

THIS MIGHT BE A GOOD TIME TO TAKE A BREAK, AND GIT COMMIT

Part 3, ‘I see a good moon Horizon’ (thats for my Creedence fans)

So far we haven’t gotten into interacting with the database. Thats okay, it’s the next part. The next step will be proving we can get stuff onto, and from the database. Let’s download the module and start refactoring:

npm install --save @horizon/client

We can go ahead and remove some extra stuff from our dist/index.html:

<!doctype html>
<html>
<head>
<meta charset=”UTF-8">
<link rel=’stylesheet’ href=’https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css'></link>
<link rel=”stylesheet” href=”./style.css”></link>
</head>
<body>
<div class=’attach’></div>
</body>
<script src=’./bundle.js’></script>
</html>

This is pretty minimal, a couple stylesheets to clean up our presentation and our bundle.js file are all we need (Notice we removed the Horizon script, it will be included with the @horizon/client module). As we start to do some ‘heavy’ lifting in our App component.

app.jsx:

import React, { Component } from ‘react’;
import Messages from ‘./messages’;
//include our newly installed horizon client
const Horizon = require(‘@horizon/client’);
const horizon = Horizon({ secure: false });
//this initiates our 'messages' collection inside of our Rethinkdb
const chat = horizon(‘messages’);
class App extends Component {//init our state with the built in constructor function
constructor(props) {
super(props);
this.state = {
author: false,
text: false
}
}
//these two handle change events will watch our form values,
//and update our state
handleChangeAuthor(event) {
this.setState({author: event.target.value});
}
handleChangeText(event) {
this.setState({text: event.target.value});
}
sendMessage() {
//check for empty strings and return early if a message/author
//isn't entered
if(this.state.text === false || this.state.author === false) {
alert(‘Invalid Submission’);
return;
}
let message = {
text: this.state.text,
author: this.state.author
};
//the store method will take our new message and store it in our
//Rethink collection
chat.store(message);
}
render() {
return (
<div>
<form>
<div className=’center’>
<button onClick={this.sendMessage.bind(this)}>Send Message</button>
<input onChange={this.handleChangeAuthor.bind(this)}></input>
<input onChange={this.handleChangeText.bind(this)}></input>
</div>
</form>
//pass chat as a prop to Messages Component for
//Querying the database for messages
<Messages chat={chat}/>
</div>
);
}
}
export default App;
  • Take care to bind the correct this value to your change handlers
  • Passing in the chat object down to the child component is how we will be able to get our messages and map them to our Message component

After adding these changes in, we’re getting messages posted to the database. One of the coolest features is something we’re going to implement in the Messages file. We won’t have to change our individual Message component because of the way we wrote it before. But, we will have to be listening and re-rendering on a new message. Horizon’s websocket infrastructure takes care of that for us in one beautiful line of code. On our chat object we have the method watch().subscribe(). This will ‘watch’ our database for changes and return a new updated list of messages when they occur.

messages.jsx:

import React, { Component } from ‘react’;
import Message from ‘./message’;
class Messages extends Component {
constructor(props) {
super(props);
this.chat = props.chat;
//init our Messages state with and empty conversation array
this.state = {
convo: []
};
}
//when our component mounts, it will call to the database with
//this.chat.watch, setting the state and re-rendering our component
//with our messages
componentDidMount() {
this.chat.watch().subscribe(
(messages) => {
let convo = messages.map(function(message) {
return message
});
this.setState({convo: convo});
},
(err) => {
console.log(err);
}
);
}
//and our mappping function that used to render our dummy data,
//now outputs our db messages
render() {
let msgsjsx = this.state.convo.map(function(message, i){
return <Message msg={message} key={i} />
});
return (
<div className=’container-fluid’>
{msgsjsx}
</div>
);
}
}
export default Messages;

At this point, if you point your browser to localhost:8181/ again, you should see something like this and your messages should send and re-render immediately(Thanks React, Rethink, Webpack and Horizon!):

At this point, you have the basics of a real time chat application sitting on top of Horizon.io and RethinkDB. And you can watch for database changes with one method made available by Horizon and the Rethink collection. I encourage people to take this repo as a stepping stone to take it to the next level. Style it, add OAuth and deploy, or whatever you think would be fun!

-Mick

source code for this can be found at: https://github.com/mickberber/reChat under the ‘tutorial’ branch

Frontend Weekly

It's really hard to keep up with all the front-end…

Frontend Weekly

It's really hard to keep up with all the front-end development news out there. Let us help you. We hand-pick interesting articles related to front-end development. You can also subscribe to our weekly newsletter at http://frontendweekly.co

Mick Berber

Written by

Front End at OneMob

Frontend Weekly

It's really hard to keep up with all the front-end development news out there. Let us help you. We hand-pick interesting articles related to front-end development. You can also subscribe to our weekly newsletter at http://frontendweekly.co

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store