Part 1 - React components

In this section we’ll cover the react components, please make sure you have checked out the sample repo so you can run the code as you read.

The application will implement three react components: a developer list, a mini profile and a pager.

MiniProfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var React = require('react');

var MiniProfile = React.createClass({
  displayName: 'MiniProfile',
  render: function() {
    var user = this.props.user;
    var css = {width:120, padding:10};
    return (
      <div className="pull-left">
        <img src={user.avatar_url} style={css} className="img-circle pull-left"/>
        <div className="text-center">
          <a href={user.html_url} target="_blank">
            <span className="label label-info">{user.login}</span>
          </a>
        </div>
      </div>
      );
    }
});

module.exports = MiniProfile;

Explanation: This component has only one dependency, so we declare a React instance on line 1. Using the react instance we proceed to create our component with React.createClass and give it the name MiniProfile. The displayName property allows the name of our component to be shown on the React plugin for chrome :) Next, our component implements the render method, with the user received as a property via this.props.user and lovely JSX. Finally, we export our MiniProfile so it’s available for inclusion on other scripts, as we’ll see on the developer list.

Pagination

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var React = require('react');
var cx = require('classnames');
var Link = require('react-router').Link;

var Pagination = React.createClass({
  displayName: 'Pagination',
  render: function() {
    var linkCss = {marginTop: 20};
    var buttonCss = {margin: 5};
    var prevPage = {page: this.props.currentPage - 1};
    var nextPage = {page: this.props.currentPage + 1};
    var prevClasses = cx({
      'glyphicon': true,
      'glyphicon-chevron-left': true,
      'btn btn-primary': true,
      'hidden': this.props.currentPage === 1
    });
    return (
      <div className="row clearfix text-center" style={linkCss}>
        <Link to="index" params={prevPage} style={buttonCss}>
          <button className={prevClasses}>
          </button>
        </Link>
        <Link to="index" params={nextPage} style={buttonCss}>
          <button className="glyphicon glyphicon-chevron-right btn btn-primary">
          </button>
        </Link>
      </div>
    );
  }
});
 

Explanation This component has two additional dependencies beside just React. On line 2 we create an instance of classnames used to add css classnames based on a condition (not showing the left chevron on the first page in this case). The second dependency is for Link a component that comes with react-router. Link will help us creating links that allow updating the browser’s url while navigating through our developer list via ajax and also work as traditional anchor tags when javascript is not available, as in the case of a search robot or a clunky browser. The JSX bit just adds a ‘previous’ and ‘next’ chevrons so we can move back and forth in the list.

Developer list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
var React = require('react');
var request = require('request');
var MiniProfile = require('./miniprofile');
var Pagination = require('./pagination');

var DeveloperList = React.createClass({
  displayName: 'DeveloperList',
  statics: {
    fetchData: function(params, callback) {
      'Fetching from Github';
      var options = {
        url: "https://api.github.com/users?since=" + (params.page * 30),
        headers: {
          'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/21.0'
        },
        withCredentials: false
      };
      return request(options, function(error, response, body) {
        callback(null, JSON.parse(body));
      });
    }
  },
  render: function() {
    var profiles = []
    this.props.data.forEach(function(user, index) {
      profiles.push(<MiniProfile key={index} user={user}/>);
    });
      
    return (
      <div>
        <div className="row">
          {profiles}
        </div>
        <Pagination currentPage={parseInt(this.props.params.page)}/>
      </div>
    );
  }
});

module.exports = DeveloperList;

Explanation: It all comes together on our developer list component. Lines 1 - 4 define the dependencies for this component, starting with React. Then we create a request instance, which we’ll use later on to make our call to Github’s API. The last to lines create instances of our previously defined MiniProfile and Pagination components. Of note here, is that we have created our data fetching function as a static, in order to be able to call it later on from React Router. The data resulting from this function will be passed by the router as a property of our developer list, so we can make use of it in line 26 in a forEach as a source to build individual MiniProfiles for each developer in our render function. To finish things off, we include our Pagination component and pass the current page as a property.

That’s it for the first part, on the next post we’ll go over the process hooking our developer list component to a router and making it work both on the server and the browser!

Introduction

In this four-part series, we’re going to cover how to go about making a PHP app benefit from the use of ReactJS components both server and client-side. The case for this is, for example, if we have a Wordpress or Drupal CMS we want to keep around for it’s content management functions, but want to extend with new and modern functionality.

Being able share some of the javascript code both on the server and client provides many advantages, such as:

  • SEO: Search engines should be able to crawl and index our content. No need to implement/pay for a pre-rendering service to achieve this.
  • Performance: A pre-rendered page can be cached server-side and provide a snappy user experience. React hooks itself up once the page is served and takes over the rendering of it’s components based on user interactions.
  • Compatibility: A browser with no javascript enabled can still display the page and allow for a simple navigation.
  • Usability: A user is be able to bookmark the current state of the page and is able to return to it or share it.
  • Maintainability: Sharing code between the server and the browser allows for a smaller code base, which is easier to keep healthy.

For these reasons, just adding React components to the existing user interface of our CMS would prevent us from reaping the full benefits, even though it would still provide our application with the reduced development times, maintainability and faster response time that React brings to the table.

Tools used

This post assumes some basic familiarity with the following:

Edit: [07/04/2015] Replaced coffeescript snippets with regular javascript (ES5) based on feedback from my good friend Luke :)

Sample application

The goal of our brave little sample app is to connect to Github’s API and fetch a list of developers and display their username and avatar. We’ll also provide a link to their Github profile page and show a simple back and forth pager at the bottom.

The series will be spread as follows:

  • Intro
  • Part 1: React components
  • Part 2: Router configuration
  • Part 3: Server-side rendering
  • Part 4: Rendering from PHP with dnode

I’ve created a repository with the finished app so you can download, analyze and tinker with it as you read along. Please be gentle, it’s my first attempt at a coding post+repo! You can find it at this repo.

Welcome to my first (and hopefully last) blog! I hope to make this a space were people can find something interesting and/or amusing as I share my adventures in the wild, wild world of software development. It started as a place to host a post I’ve been working on for the last couple of weeks about reactjs after a friend suggested me to give it a go. Once that post is live, I hope to add more entries as new challenges come along and present opportunities to learn more cool stuff to share :)

To know more about me (not that there’s any perceivable benefit or pleasure to be derived therefrom), check out the about page.