Eric Escalante Blogging gingerly

Isomorphic React apps in PHP via dnode - 1

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 var React = require('react');
 2 
 3 var MiniProfile = React.createClass({
 4   displayName: 'MiniProfile',
 5   render: function() {
 6     var user = this.props.user;
 7     var css = {width:120, padding:10};
 8     return (
 9       <div className="pull-left">
10         <img src={user.avatar_url} style={css} className="img-circle pull-left"/>
11         <div className="text-center">
12           <a href={user.html_url} target="_blank">
13             <span className="label label-info">{user.login}</span>
14           </a>
15         </div>
16       </div>
17       );
18     }
19 });
20 
21 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 var React = require('react');
 2 var cx = require('classnames');
 3 var Link = require('react-router').Link;
 4 
 5 var Pagination = React.createClass({
 6   displayName: 'Pagination',
 7   render: function() {
 8     var linkCss = {marginTop: 20};
 9     var buttonCss = {margin: 5};
10     var prevPage = {page: this.props.currentPage - 1};
11     var nextPage = {page: this.props.currentPage + 1};
12     var prevClasses = cx({
13       'glyphicon': true,
14       'glyphicon-chevron-left': true,
15       'btn btn-primary': true,
16       'hidden': this.props.currentPage === 1
17     });
18     return (
19       <div className="row clearfix text-center" style={linkCss}>
20         <Link to="index" params={prevPage} style={buttonCss}>
21           <button className={prevClasses}>
22           </button>
23         </Link>
24         <Link to="index" params={nextPage} style={buttonCss}>
25           <button className="glyphicon glyphicon-chevron-right btn btn-primary">
26           </button>
27         </Link>
28       </div>
29     );
30   }
31 });

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 var React = require('react');
 2 var request = require('request');
 3 var MiniProfile = require('./miniprofile');
 4 var Pagination = require('./pagination');
 5 
 6 var DeveloperList = React.createClass({
 7   displayName: 'DeveloperList',
 8   statics: {
 9     fetchData: function(params, callback) {
10       'Fetching from Github';
11       var options = {
12         url: "https://api.github.com/users?since=" + (params.page * 30),
13         headers: {
14           'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/21.0'
15         },
16         withCredentials: false
17       };
18       return request(options, function(error, response, body) {
19         callback(null, JSON.parse(body));
20       });
21     }
22   },
23   render: function() {
24     var profiles = []
25     this.props.data.forEach(function(user, index) {
26       profiles.push(<MiniProfile key={index} user={user}/>);
27     });
28       
29     return (
30       <div>
31         <div className="row">
32           {profiles}
33         </div>
34         <Pagination currentPage={parseInt(this.props.params.page)}/>
35       </div>
36     );
37   }
38 });
39 
40 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!