About the Author:

Using Horizon/RethinkDB with React

August 9th, 2017

Horizon is an open source Backend as a Service (BaaS) that allows developers to easily build data-driven web and mobile applications. Horizon, which is built on RethinkDB and by the RethinkDB team, facilitates common application development tasks like backend setup, real-time data transfer, security and scalability. Horizon can be run locally, on a private server or in the cloud.

This tutorial will give you an introduction to Horizon and RethinkDB while also showing you how to build a simple app with Horizon and React. This will give you a simple and scalable architecture for building your own much more complex apps.

A Quick Note About the State of Horizon/RethinkDB

On October 5th 2016, the company behind RethinkDB announced it would be shut down. This left many users believing the entire project was being cancelled and shuttered. In actuality, RethinkDb and Horizon became fully open source and community driven. On February 6th 2017, the Cloud Native Computing Foundation (CNCF) purchased the rights to the RethinkDB source code and contributed it to the Linux Foundation. This gave the project the necessary support to keep the project alive and facilitate its continuous development.

Horizon + React = Awesome Combo

Horizon and React is a pair made in heaven for developers. Horizon takes charge of all the backend complexities and React facilitates building a modular, reactive frontend. Horizon handles the distribution of data in real-time while React keeps the clients updated. Everything scales and everything has high performance and low latency.

When there is a change in the app’s state, ReactJS will efficiently redraw the necessary components by diffing between the virtual DOM and the existing DOM (and making only the required changes). Horizon notifies all subscribed clients when there is a data update, so they can either download the data again or simply apply the change into its local copy when the original data is too large to fully reload on each change. Together, Horizon and React allow for a simple way to build real-time data-driven applications.

Building a ‘ToBring’ App

To demonstrate how they work together, we’ll build a ‘ToBring’ app (not another another ToDo!). This application will track a list of items we need for a party. This app will allow users to add items in a section that we will call “We need list” or move them to another list called “We have list” and add the name of the user who will bring the item.

To see all the code for this demo, check out this repo.

To keep the setup simple, we’ll use create-react-app. To start the React part of the application, first install create-react-app with the command

npm install -g create-react-app

Then you’ll need to initialize a new app with the commands

create-react-app party_checklist

and

an npm install/start

Once ready, it should launch on your browser with the address http://localhost:3000/ and will auto-reload every time you change the source code.

React is all about components. The main component of our app will have three sub-components: one to add a new item, one to hold the “we need list” and another to hold the “we have list”. Here’s a quick visual of how these components will fit together.

We will need to know the user’s name for keeping track of what they bring. Let’s put a simple input to get the name of the active user into the main component.

If you are wondering, Horizon does include options to authenticate, add permissions and even Transport Layer Security to our app, but that is beyond the scope of this tutorial.

Here’s how we’ll code up the name input:

app.js

import React, { Component } from 'react';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      // A holder for the user name
      user : '',
    };
    // A binding to call updateUser without the () suffix
    this.updateUser = this.updateUser.bind(this);
  }

  render() {
    return (
      <div
        className= 'app'>
        <h1>Party Checklist</h1>
        <p>I am:</p>
        <input
          value = {this.state.user}
          onChange = {this.updateUser} />
      </div>
    );
  }

  // A method to update the state in response to a synthetic event
  updateUser(e) {
    this.setState({user: e.target.value});
  }
}

It is important to remember how we use the constructor to hold the state and also to create bindings to expose methods, without it “this” will return “undefined” when the method is actually called. This is how React handles events, also notice how “updateUser” has a parameter called “e”, this is called a synthetic event.

Now let’s add the NewItem component:

app.js

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      user : '',
    };
    this.updateUser = this.updateUser.bind(this);
  }

  render() {
    return (
      <div
        className= 'app'>
        <h1>Party Checklist</h1>
        <p>I am:</p>
        <input
          value = {this.state.user}
          onChange = {this.updateUser} />
        <NewItem />
      </div>
    );
  }

  updateUser(e) {
    this.setState({user: e.target.value});
  }
}

class NewItem extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      item  : '',
    };
    this.updateItem = this.updateItem.bind(this);
  }
  render() {
    return (
      <div>
        <p>Add to need list:</p>
        <form>
          <input
            value = {this.state.item}
            onChange = {this.updateItem} />
        </form>
      </div>
    );
  }

  updateItem(e) {
    this.setState({item: e.target.value});
  }
}

JSX allows you import the “NewItem” component into “App” similar to custom web components on HTML.

Now let’s make a component for the WeNeedList, notice how it was an “items” list in its properties, this means the value will be passed when it is declared inside another component. When dealing with lists in React, you can use a simple map method.

app.js

class WeNeedList extends React.Component {
  render() {
    return (
      <div>
        <h2>We need...</h2>
        {this.props.items.map(item => (
          <div
            className= 'row'>
            <button
              className= 'remove'>
              x
            </button>
            {item.description}
            <button
              className= 'add'>
              +
            </button>
          </div>
        ))}
      </div>
    );
  }
}

Now lets add “WeNeedList” to “App”.

app.js

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      weNeedList : [],
      user : '',
    };
    this.updateUser = this.updateUser.bind(this);
  }

  render() {
    return (
      <div
        className= 'app'>
        <h1>Party Checklist</h1>
        <p>I am:</p>
        <input
          value = {this.state.user}
          onChange = {this.updateUser} />
        <NewItem />
        <WeNeedList
          items = {this.state.weNeedList}
          user = {this.state.user} />
      </div>
    );
  }

  updateUser(e) {
    this.setState({user: e.target.value});
  }
}

See how “App”will hold the state of the list and will pass it to “WeNeedList” as parameters. Inside “WeNeedList” we can read this values from props.

And finally we need to make a WeHaveList component:

app.js

class WeHaveList extends React.Component {
  render() {
    return (
      <div>
        <h2>We have...</h2>
        {this.props.items.map(item => (
          <div
            className= 'row'
            key = {item.id}>
            <button
              className= 'cancel'>
              x
            </button>
            {item.description} from {item.user}
          </div>
        ))}
      </div>
    );
  }
}

And also add it to the “App” component:

app.js

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      weNeedList : [],
      weHaveList : [],
      user : '',
    };
    this.updateUser = this.updateUser.bind(this);
  }

  render() {
    return (
      <div
        className= 'app'>
        <h1>Party Checklist</h1>
        <p>I am:</p>
        <input
          value = {this.state.user}
          onChange = {this.updateUser} />
        <NewItem />
        <WeNeedList
          items = {this.state.weNeedList}
          user = {this.state.user} />
        <WeHaveList
          items = {this.state.weHaveList} />
      </div>
    );
  }

  updateUser(e) {
    this.setState({user: e.target.value});
  }
}

Now the core of our app is complete! But it doesn’t do anything yet, for that, we need to add Horizon and create a few methods inside our components.

To install Horizon in your machine, simply run the command npm install -g horizon.

Horizon, like create-react-app, automatically initializes everything. You only need to provide the name of the app you want to create with the command hz init party_checklist; it is important that you run this from the same place from where you created the app, so it will just add new files to the project instead of creating a new one. You must always start with React and then add Horizon.

Horizon needs RethinkDB to work. Before launching your Horizon server, make sure you have RethinkDB running on your machine. Refer to the official documentation for detailed instructions on how to run it on your system. The simplest way for Mac users is to use Homebrew to install it.

Now let’s configure Horizon to look for RethinkDB in your local machine. To do so, go to .hz => config.toml and replace it with the following:

bind = [ "localhost" ]
port = 8181

connect = "localhost:28015"
start_rethinkdb = false

To launch your Horizon server locally, go the folder with your project and run hz serve –dev, this will start your local Horizon server on port 8181. Note that you always need to keep one instance running React and another running Horizon while you are developing.

Before hitting the code, let’s learn a few basic Horizon concepts:

Horizon groups data into horizon collections and each collection is backed by a RethinkDB table. All documents inside a collection are identified by a unique key stored in the id field.

Horizon provides a very simple yet powerful API that helps you handle the following aspects:

In this tutorial, we will be focusing on the first two, DB connection and data collections.

ReactJS is designed to use an unidirectional data flow, so the data always flow from the DB into the parts of the UI that represents it. You can stream data right into its specific component or stream many into the parent and then your pass it down via component props.

Here’s a visual to help you make sense of the data flow in this app:

Note that any change in the data is sent from one React client into Horizon and Horizon sends it back to all React apps so they can update their view, this flow makes sure that all clients are always watching the same without inconsistencies. Also note that the data always flows in one direction between Horizon and React.

Let’s add the Horizon client library into our app. First run npm install @horizon/client to get the horizon client installed locally and make an instance of the client holding a connection to our local server in app.js.

app.js

import Horizon from '@horizon/client';
const horizon = new Horizon({host: 'localhost:8181'});

Now let’s configure React to open a connection with the Horizon server right after everything has loaded correctly using the componentDidMount() lifecycle method. We will include some watchers to log when the connection to Horizon is ready and when it gets disconnected.

app.js

class App extends Component {

  // ...

  componentDidMount(){
    horizon.connect();

    horizon
      .onReady()
        .subscribe(() =>
          console.info('Connected to Horizon server'));

    horizon
      .onDisconnected()
        .subscribe(() =>
          console.info('Disconnected from Horizon server'));
  }
}

Remember how we talked about collections in Horizon? Our app will use two Horizon collections, one for the “we need list” and other for the “we have list”. Let’s store a reference to them at the top of app.js.

app.js

import Horizon from '@horizon/client';

const horizon = new Horizon({host: 'localhost:8181'});
const weNeedList_collection = horizon('weNeedList');
const weHaveList_collection = horizon('weHaveList');

Horizon makes it very easy to watch and apply for changes in real time, you can even add custom logic and subscribe to a feed of changes. A common practice is to use the timestamp of each object as its ID and request the collection sorted by the ID, so they will always come in chronological order. Let’s do that for both collections.

app.js

class App extends Component {

  // ...

  componentDidMount(){
    horizon.connect();

    // ...

    weNeedList_collection
      .order('id')
        .watch()
          .subscribe(allItems =>
            this.setState({weNeedList: allItems}),
            error => console.error(error));

    weHaveList_collection
      .order('id')
        .watch()
          .subscribe(allItems =>
            this.setState({weHaveList: allItems}),
            error => console.error(error));
  }

Now it’s time to add data manipulation methods, for this app we will use insert and remove; in both cases we only need to pass the item we wish to insert or remove as the parameter.

Let’s start by updating NewItem

app.js

class NewItem extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      item  : '',
    };
    this.updateItem = this.updateItem.bind(this);
    this.insertItem = this.insertItem.bind(this);
  }
  render() {
    return (
      <div>
        <p>Add to need list:</p>
        <form
          onSubmit = {this.insertItem}>
          <input
            value = {this.state.item}
            onChange = {this.updateItem} />
        </form>
      </div>
    );
  }

  updateItem(e) {
    this.setState({item: e.target.value});
  }

  insertItem(e) {
    e.preventDefault();
    const newItem = {
      description: this.state.item,
      id: Date.now()
    };

    weNeedList_collection.insert(newItem);
    this.setState({item: ''});
  }
}

Now let’s do the same for WeNeedList.

app.js

class WeNeedList extends React.Component {
  render() {
    return (
      <div>
        <h2>We need...</h2>
        {this.props.items.map(item => (
          <div
            className= 'row'>
            <button
              className= 'remove'
              onClick= {() => this.remove(item)}>
              x
            </button>
            {item.description}
            <button
              className= 'add'
              onClick= {() => this.moveToHave(item)}>
              +
            </button>
          </div>
        ))}
      </div>
    );
  }

  remove(item) {
    weNeedList_collection.remove(item);
  }

  moveToHave(item) {
    weNeedList_collection.remove(item);
    item.user = this.props.user;
    weHaveList_collection.insert(item);
  }
}

And finally for WeHaveList.

app.js

class WeHaveList extends React.Component {
  render() {
    return (
      <div>
        <h2>We have...</h2>
        {this.props.items.map(item => (
          <div
            className= 'row'
            key = {item.id}>
            <button
              className= 'cancel'
              onClick= {() => this.returnToNeed(item)}>
              x
            </button>
            {item.description} from {item.user}
          </div>
        ))}
      </div>
    );
  }

  returnToNeed(item) {
    weHaveList_collection.remove(item);
    weNeedList_collection.insert(item);
  }
}

And we are finished! Now you know all the basics to start building amazing real time apps using ReactJS and Horizon.

The Horizon-React combo greatly simplifies development, allowing you and your team to develop applications and prototypes in a very short time while also making it easy to scale, maintain and debug those apps in the future, this greatly reduces costs and makes you and your team more competitive in the global market.

About the Author:

Creating a Realtime Pokedex App with React and RethinkDB

September 8th, 2016

In this tutorial you’re going to create a realtime Pokedex app with React and RethinkDB. The app will allow users to look for a Pokemon by typing on a search field. If a Pokemon is not found, it will ask if they want to add it. If the user chooses yes, it will make a request to the server. The server will then talk to the Pokemon API to look for the Pokemon. If it exists, the server will save the data to RethinkDB. At this point, RethinkDB’s changefeeds feature will be triggered and it will send the new data to all connected clients using Socket.io. This will update the UI to show the newly added Pokemon to all users.

Here’s what the app is going to look like:

screen-shot-2016-09-07-at-5-12-28-pm

No previous knowledge of React or RethinkDB is required for this tutorial. So I’m going to provide as much detail as I can.

Steps We’ll Take in This Tutorial

  1. Install RethinkDB
  2. Setup a RethinkDB database and table
  3. Create the app
    1. Install the Dependencies
    2. Create the ListItem Component
    3. Create the Server Component

The final code for this tutorial can be found in this Github repository

Setting Up Your Environment

If you don’t want to pollute your OS with lots of installs, you can use the Scotchbox Vagrant box to install all the dependencies for this app.

Installing RethinkDB

You can install RethinkDB by following the installation instructions for your OS on the official docs. However, if you have already Docker installed, you can also run the RethinkDB docker image with the following:

docker run -d -P –name rethink1 rethinkdb

Once the image is installed, it will return the container ID, at this point you can run RethinkDB with the following:

docker start <container ID>

Setting Up the Database

Once you got RethinkDB running, access the admin console in your browser by going to https://localhost:32770. If you used vagrant, be sure to replace localhost with the ip address assigned to it.

Click on tables on the main navigation then click on add database. Enter pokedex as the name of the database. Once it’s created, add a new table named pokemon. This will be the table that you’re going to work on throughout the tutorial.

Creating the App

The app is going to have two components: the actual React app and the server component. You’re going to create the app first. This will reside inside the app folder of your working directory. You can check the project on Github to have an idea of the directory structure.

Installing the Dependencies

You can install the dependencies by creating a package.json file and add the following:

  {"name": "pokedex-app",

  "version": "1.0.0",

  "description": "",

  "main": "index.js",

  "scripts": {

"compile": "browserify -t [ babelify --presets [ react es2015 ] ] src/app.js -o js/app.js"

  },

  "author": "",

  "license": "ISC",

  "dependencies": {

"aja": "^0.4.1",

"picnic": "^6.1.5",

"react": "^15.3.1",

"react-dom": "^15.3.1",

"react-image-fallback": "^3.1.1",

"react-loading": "0.0.9",

"react-tooltip": "^3.1.6",

"socket.io-client": "^1.4.8"

  },

  "devDependencies": {

"babel-preset-es2015": "^6.13.2",

"babel-preset-react": "^6.11.1",

"babelify": "^7.3.0",

"browserify": "^13.1.0"

  }

}

Execute npm install to install all of the dependencies. Here’s an overview of what each one does:

  • aja – for making http requests to the server.
  • picnic – for beautifying the app.
  • react – for creating UI components.
  • react-dom – for rendering React components into the DOM.
  • react-image-fallback – for showing a fallback image in case the Pokemon images fails to load.
  • react-loading – for showing a loading animation while saving a new Pokemon data.
  • react-tooltip – for generating tooltips that shows the Pokemon description.
  • socket.io-client – for talking to socket.io.

And here’s what each devDependencies does:

  • browserify – puts all the files together in a single file so you can link to it on your page.

You could actually use Webpack to achieve the same thing plus more features such as live-reloading, but to keep things simple, I’m going to leave that for another day.

Once you’re done installing those, create a src and src/components directory. This is where the React components will reside. Inside the src directory, create an app.js file and import the libraries that you’ve just installed:

import React, {Component} from 'react';

import ReactDOM from 'react-dom';

import ListItem from './components/ListItem';



import io from 'socket.io-client/socket.io';



import Loading from 'react-loading';

import aja from 'aja';



Create the Main component:



class Main extends Component {



}

Inside is the constructor where you create a socket connection to the server component and declare the default state, because this function gets executed once this component is initialized. In React, the state is used to store component data that changes over time. Every time the state is updated, the component is re-rendered to reflect the changes. You can read more about this in Thinking in React.

constructor(props) {



  super(); //execute the initialization code of Component class

  this.base_url = 'https://192.168.33.10:3000'; //base URL of the server component



  //open a Socket connection to the server

  this.socket = io(this.base_url, {

transports: ['websocket']


  });

  //declare the default state

  this.state = {

search_term: '', //default value for the search field

pokemon_list: [], //array of Pokemon data from the server

filtered_pokemon_list: [], //filtered Pokemon data based on search_term

is_loading: false //whether the app is waiting for the server or not.

  };



}

The componentWillMount() is one of the component lifecycle methods in React. It gets executed right before this component is rendered into the DOM. This makes it the perfect candidate for listening for server updates and fetching the pokemon list from the server.

componentWillMount() {

  //executes when the pokedex_updated event gets emitted by the server

  this.socket.on('pokedex_updated', (data) => {

    

var pokemon_list = this.state.pokemon_list; //get the current pokemon list from the state

if(data.old_val === null){ //if its a new item

   pokemon_list.push(data.new_val); //add the new item into the temporary variable

   //actually update the state using the temporary variable for storing state data

   this.setState({

     pokemon_list: pokemon_list

   }, () => {//executed when the state is updated

     //filter the pokemon list based on the search term

     this.filterPokemonList.call(this, this.state.search_term, pokemon_list);

   });

}

  });



  this.getPokemonList(); //get list of pokemon from the server

}

The filterPokemonList() function is responsible for updating the state with a list of filtered Pokemon based on the search term inputted by the user.

filterPokemonList(search_term, pokemon_list) {



  var filtered_pokemon_list = pokemon_list.filter((pokemon) => {//loop through the pokemon list

if(pokemon.name.indexOf(search_term) !== -1){//check if search_term is within the pokemon name

   return pokemon;

}

  });

  this.setState({filtered_pokemon_list}); //update the state with the filtered list

}

The getPokemonList() function performs a GET request to the /pokemon route in the server. This route returns an array containing all the Pokemon data that were previously saved to RethinkDB. Once a response comes back, the state is updated using the returned data. The pokemon list and the filtered one is the same at this point since the user hasn’t typed anything on the search field yet.

getPokemonList() {



  aja()

.method('get')

.url(this.base_url + '/pokemon')

.on('200', (pokemon_list) => { //request returned a success response header

   this.setState({ //update the state with the pokemon list

     pokemon_list: pokemon_list,

     filtered_pokemon_list: pokemon_list

   });

})

   .go();



}

The render() method is the only required method when creating a component with React. It’s what returns the actual UI of the component. The UI is represented using HTML. And most importantly, a React component can only return a single root element.

render() {



  return (

<div>

   <div id="header">

     <h1>RethinkReact Pokedex</h1>

     <input type="text" name="pokemon" id="pokemon" onChange={this.searchPokemon.bind(this)} placeholder="What Kind of Pokemon are you?" />

   </div>

   <div className="pokemon-list flex">

   {this.state.filtered_pokemon_list.map(this.renderListItem.bind(this))}

   </div>

   <div className={this.state.is_loading ? 'loader' : 'loader hidden'}>

     <Loading type="bubbles" color='#f93434' />

   </div>

   {this.renderNoResults.call(this)}

</div>

  );



}

Breaking down the code above, you have a text field for entering the name of the Pokemon you’re looking for. The onChange attribute is used to specify the function to be executed when the value of the text field changes. In this case, the searchPokemon function is executed. There’s a need to use bind instead of simply calling it directly because methods in es6 aren’t automatically binded to the class. Below the search field is the container for the Pokemon list. This uses the filtered Pokemon list as the data source for the map function. The map function goes through the Pokemon list and calls the renderListItem() function for every iteration.

Below the Pokemon list is the container for the loading animation. The current value of the is_loading property is used to control the class name to use. A hidden class is added to hide the container if the component is not in the is_loading state.

The renderNoResults() function is called. This function renders the button for adding the Pokemon to the database. It only renders when the filtered Pokemon list contains nothing. The button when clicked calls the savePokemon() function.

renderNoResults() {



  if(!this.state.filtered_pokemon_list.length){

return (

   <div className={this.state.is_loading ? 'no-result hidden' : 'no-result'}>

     Sorry, I cannot find that Pokemon. Would you like to add it? <br />

     <button onClick={this.savePokemon.bind(this)}>yes!</button>

   </div>

);

  }



}

The savePokemon() function is the one that’s making a request to the server for saving the Pokemon into the database.

savePokemon() {

  //enable loading state

  this.setState({

is_loading: true

  });

  //check if user is searching for a blank string, only entertain the request if it’s not

  if(this.state.search_term.trim() != ''){    

//make a POST request to the server

aja()

   .method('post')

   .url(this.base_url + '/save')

   .data({name: this.state.search_term}) //add the name of the pokemon

   .on('200', (response) => {

     if(response.type == 'fail'){//if the request failed, inform the user

       alert(response.msg);

     }

     //disable loading state

     this.setState({

       is_loading: false

     });

   })

   .go();

  }

}

Next is the searchPokemon() function. As you’ve seen earlier, this gets executed every time the user types in something on the search field.

searchPokemon(e) {

  //e is the event, e.target refers to the search field, e.target.value is the current value of the search field

  let search_term = e.target.value.toLowerCase(); //convert to lower case

  this.setState({search_term}); //update the state with the search term

  this.filterPokemonList.call(this, search_term, this.state.pokemon_list); //filter the Pokemon list based on the search term.



}
The renderListItem() function gets called for every iteration of the map function inside the render method. The data of the specific Pokemon in the array item is passed in as an argument to this function. You then use it to supply data to the ListItem component by means of passing in props. Props are pretty much like your normal attribute, but they can be used inside the component by referring to them as this.props.{prop name}. In this case, pokemon is a prop and you're using the pokemon data as its value. key on the other hand is also a prop, but it serves a special purpose in React. And that is to uniquely identify each item in the array. This helps React in keeping track of each element whenever you need to sort, delete or add new elements into the array.

renderListItem(pokemon) {



  return (

<ListItem pokemon={pokemon} key={pokemon.id} />

  );



}

Creating the ListItem Component

As you’ve seen earlier, the ListItem component is used to render each Pokemon data. Create a ListItem.js file inside the src/components directory:

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

import ReactImageFallback from "react-image-fallback";

import ReactTooltip from 'react-tooltip';

In the above code, aside from Components which you’ve used earlier, PropTypes is also extracted from react. You’ll use this later on to specify which props you expect consumers of this component should specify.

Create the component, this time you need to export it so that it can be used on other files.

export default class ListItem extends Component {}

 

The render() function returns a list item. It has a data-tip attribute which is used by react-tooltip for the contents of the tooltip. The tooltip becomes visible on click to cater for touch devices. Below it is the ReactImageFallback component which takes in the Pokemon image, the fallback image in case it fails to load, and an initial image to display while the image is loading. And below that is the Pokemon name and its types. Inside the types div is where the array of types is rendered.

render() {

  let {id, name, sprite, description, types} = this.props.pokemon;

  let sprite_img = `https://192.168.33.10:3000/img/${sprite}`;

  let loader_img = "img/loader.gif";

  let fallback_img = "img/pokeball.png";

  return (

<li className="pokemon" data-tip={description} data-event="click">

   <ReactTooltip place="bottom" type="dark" effect="float" class="tooltip" />

   <ReactImageFallback

     src={sprite_img}

     fallbackImage={fallback_img}

     initialImage={loader_img}

     alt={name}

     className="pokemon-sprite" />

   <div className="pokemon-name">{name}</div>

   <div className="types">

     {types.map(this.renderTypes.bind(this))}

   </div>

</li>

  );

}

Here’s the renderTypes() function:

renderTypes(type) {

  return (

<div className={type} key={type}>

{type}

</div>

  );

}

The Pokemon type is used as the value for the key because it’s unique.

Define the props that you want to be passed in to this component. In this case there’s only a single one and should be an object.

ListItem.propTypes = {

  pokemon: PropTypes.object.isRequired

};

If this prop isn’t passed in, a warning will show up in the console. If you want to learn more, you can read more about this on the documentation for prop validation.

Next, define the styles css/styles.css:

/*for centering the header*/

#header {

margin: 0 auto;

text-align: center;

width: 300px;

}



/*for adding white space around the edges*/

#main {

padding: 10px;

}

/*added to components that you want to hide*/

.hidden {

    display: none;

}

/*to keep the css loader centered*/

.loader {

width: 70px;

margin: 0 auto;

}



.no-result {

width: 300px;

padding: 10px;

margin: 0 auto;

}



.types {

text-align: center;

font-size: 14px;

}



.types div {

color: #fff;

display: inline-block;

padding: 5px;

}



li.pokemon {

list-style: none;

margin: 0;

padding: 0;

text-align: center;

}



.tooltip {

width: 200px;

}



.description {

font-size: 14px;

display: none;

}



.pokemon-name {

font-weight: bold;

text-transform: capitalize;

margin-bottom: 10px;

}

/*background color to distinguish between different pokemon types*/

.normal {

background-color: #8a8a59;

}



.fire {

background-color: #f08030;

}



.water {

background-color: #6890f0;

}



.electric {

background-color: #f8d030;

}



.grass {

background-color: #78c850;

}



.ice {

background-color: #98d8d8;

}



.fighting {

background-color: #c03028;

}



.poison {

background-color: #a040a0;

}



.ground {

background-color: #e0c068;

}



.flying {

background-color: #a890f0;

}



.psychic {

background-color: #f85888;

}



.bug {

background-color: #a8b820;

}



.rock {

background-color: #b8a038;

}



.ghost {

background-color: #705898;

}



.dragon {

background-color: #7038f8;

}



.dark {

background-color: #705848;

}



.steel {

background-color: #b8b8d0;

}



.fairy {

background-color: #e898e8;

}

To put it all together, create the actual app page (index.html):

<!DOCTYPE html>

<html lang="en">

  <head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1">

<title>RethinkReact Pokedex</title>

<link rel="stylesheet" href="node_modules/picnic/picnic.min.css">

<link rel="stylesheet" href="css/style.css">

  </head>

  <body>

<div id="main"></div>

<script src="js/app.js"></script>

  </body>

</html>

As you can see, it’s pretty lightweight since all the UI stuff were already defined in React. All of the styles should be linkable since you already installed and created them earlier. The only thing that hasn’t been created yet is the js/app.js file. This is the compiled version of all the JavaScript files that you previously worked on. In the package.json file earlier, there’s the property called scripts:

"scripts": {

"compile": "browserify -t [ babelify --presets [ react es2015 ] ] src/app.js -o js/app.js"

},

This is where you can define commonly used npm scripts. In this case, a compile script is defined. This is responsible for compiling all the files inside the src directory to the js/app.js file. Browserify is the main script being used here, -t is used for defining the transforms to be utilized for compiling the app. This is where babelify and its presets comes in. They’re responsible for transforming the JSX and es6 code to JavaScript code that can run in modern browsers.

You can run the compile script using the following command:

npm run compile

Once the script is done, you should have a js/app.js file in your working directory.

Server Component

Navigate outside the app directory, create a package.json file and add the following:

{

  "name": "rethinkdb-react-pokedex",

  "version": "1.0.0",

  "description": "",

  "main": "index.js",

  "author": "",

  "license": "ISC",

  "dependencies": {

"body-parser": "^1.15.2",

"cors": "^2.7.1",

"express": "^4.14.0",

"pokedex-promise-v2": "^3.0.1",

"rethinkdb": "^2.3.2",

"socket.io": "^1.4.8"

  }

}

Execute npm install to install all the dependencies. Here’s what each one does:

  • body-parser – for parsing the request body for any JSON data.
  • cors – for allowing http requests from any host.
  • express – for creating the web server.
  • pokedex-promise-v2 – for easily making requests to the Pokemon API.
  • rethinkdb – for talking to RethinkDB.
  • socket.io – this handles real time connection between the server and the user’s browser by means of the WebSocket protocol. We’re using it to send new Pokemon data from the server to the client in real time.

Create a server.js file and add the following:

var r = require('rethinkdb'); //for talking to RethinkDB



var http = require('https'); //for making requests to Github for the Pokemon sprite

var fs = require('fs'); //for creating the file for the Pokemon sprite



//for talking to pokemon api

var Pokedex = require('pokedex-promise-v2');

var P = new Pokedex();



var express = require('express'); //for creating a web server

var app = express();



var cors = require('cors'); //for allowing cross-origin resource sharing on this server



var server = require('http').createServer(app); //create web server

var io = require('socket.io')(server); //attach socket.io to the server so it runs on the same port as the server. This means that the server will also have all the event listeners attached to socket.io, making it possible to send and receive data through the same port that the server runs in.



var bodyParser = require('body-parser'); //for parsing all sorts of request data



app.use(cors()); //allow cors in this server



app.use(bodyParser.json()); //for parsing json data

app.use(bodyParser.urlencoded({ extended: true })); //for parsing urlencoded data

Connect to RethinkDB and listen for changes on the pokemon table inside the pokedex database. If a change happens (e.g. a row is deleted/updated, a new row is created), loop through the changed data and emit a pokedex_updated event for each one. The row contains an old_val and a new_val. If it’s an insert operation, the old_val is null, if it’s an update operation, old_val contains the value from before the row was updated.

var connection; //rethinkDB connection



r.connect({host: 'localhost', port: 32769}, function(err, conn){

  if(err) throw err;

  connection = conn;

  //listen for any changes to the pokemon table

  r.db('pokedex').table('pokemon')

.changes()

.run(connection, function(err, cursor){

   if (err) throw err;

   io.sockets.on('connection', function(socket){

     cursor.each(function(err, row){ //loop through all the changed data

       if(err) throw err;

       io.sockets.emit('pokedex_updated', row); //emit an event to the clients  

     });

   });

  });

});

The /save route accepts the name of the Pokemon passed in from the request body. This is used to determine if it already exists in the database.

app.post('/save', function(req, res){



    var pokemon = req.body.name; //name of pokemon

    //check if Pokemon already exists in the database

    r.db('pokedex').table('pokemon').filter(r.row('name').eq(pokemon)).

run(connection, function(err, cursor) {

   if (err) throw err;

   cursor.toArray(function(err, result) { //convert cursor to an array

     if (err) throw err;

     if(result.length){ //pokemon already exists

     res.send({type: 'fail', msg: 'Pokemon already exists'}); //send failure message back to the client

     return;

     }

   });

});

    

});

After that, a request is made to the pokemon endpoint of the API to get the types (e.g. water, electric) of the Pokemon. Here you’re using the pokedex-promise-v2 library to easily get the details that you want. Once a response comes back, construct the object that contains the data that you want and then return it. You will be using promise.all later on to extract this data.

    //get data on pokemon type as well as the sprite

    var p1 = P.getPokemonByName(pokemon).then(function(response){

     

        var name = response.name;

        var sprite = response.sprites.front_default;

        var filename = 'public/img/' + name + ".png";

   var file = fs.createWriteStream(filename);

   var request = http.get(sprite, function(response){ //download the image from its source

     response.pipe(file); //save the image to its destination

      });



        var types = []; //array for storing the types (e.g. electric, water)

        response.types.forEach(function(row){

       types.push(row.type.name); //only push the name

        });

       //construct object to be saved to database

        var data = {

     name: name,

       sprite: name + ".png",

       types: types

        };



        return data;



})

.catch(function(error){

        res.send({type: 'fail', msg: 'Pokemon not found'}); //unknown pokemon was entered

        return;

});

Another request is also performed to the pokemon-species endpoint to get the Pokemon description. Again, extract the data that you want and then return it.

   //get description/flavor text data.

    var p2 = P.getPokemonSpeciesByName(pokemon).then(function(response){

            //only return the description of this specific pokemon in the alpha sapphire game and the language is english. This returns an array containing a single item which matched the condition 

     var result = response.flavor_text_entries.filter(function(row){

     return row.version.name == 'alpha-sapphire' && row.language.name == 'en';

     });

     var description = result[0].flavor_text;

     return description;

    })

    .catch(function(error) {

      res.send({type: 'fail', msg: 'Pokemon not found'});

      return;

    });

Both requests returns a promise so Promise.all is used to listen for when both promises has been resolved. At this point you know that all the data that you need has been acquired. So you can now save the data to RethinkDB. You can do that by calling the insert method and passing in an array containing the object that you want to save.

 //executed when both promises are resolved

    Promise.all([p1, p2]).then(function(response){

     var basic_info = response[0];

     var description = response[1];

     basic_info.description = description; //merge the second response

     //save to RethinkDB

     r.db('pokedex').table('pokemon').insert([

       basic_info,

     ]).run(connection, function(err, result){

       if(err) throw err;

    //send an success response

     res.send({

     type: 'ok'

     });

     });

    });

The /pokemon route returns all the data from the pokemon table:

app.get('/pokemon', function(req, res){

  res.header("Content-Type", "application/json");

  r.db('pokedex').table('pokemon')

.run(connection, function(err, cursor) {

   if (err) throw err;

   cursor.toArray(function(err, result) {

     if (err) throw err;

     res.send(result);

   });

  });

});

Finally, run the server on port 3000:

app.use(express.static(‘public’)); //specify directory for static assets

server.listen(3000); //run server on port 3000

Now you can access the app on your browser and add some Pokemon. For me it’s in https://192.168.33.10/rethinkreact-pokedex/app/index.html.

If you didn’t setup a server, you can just access the html file in the browser.

Summary

In this tutorial you learned how to use React, RethinkDB and Socket.io to create a Realtime Pokedex app. In a nutshell, RethinkDB is a NoSQL database which stores data as JSON documents. It also provides real time capabilities by means of its changefeeds feature. But since RethinkDB is just a database, you needed to use Socket.io in order to propagate the changes to all connected clients.

For further improvement of the app, you can try to add more data from the Pokemon API. And if you’re playing Pokemon Go, you can also add a Pokemon location feature where Pokemon locations are plotted in a Google map in real-time. You would need Geospatial queries for this one which RethinkDB supports by default.