Thinking in react
Released: 27 April, 2014
Length: 15m15s
Thinking in react
1882 views
 
We build a sample app in the Facebook view library, following along with their 'Thinking in React' guide
Hendrik Swanepoel

Hendrik lives in Cape Town, South Africa. He likes coding, designing, teaching and learning. @hendrikswan

Resources

The 'thinking in react' guide: http://facebook.github.io/react/docs/thinking-in-react.html

Setting up the environment

We create a new folder and cd into it

terminal
  
  $ mkdir ttreact && cd $_
  

We install react and bootstrap with bower

terminal
  
  $ bower install react
  $ bower install bootstrap
  

Now start up a little web server

terminal
  
  $ python -m SimpleHTTPServer
  

Lastly we create a new file in this folder called index.html

Step 0 of 'Thinking in React'

We introduce some static HTML that we would like to work to using React

index.html
  
<!doctype html>
<html lang="en">
<head>
    <link  href="bower_components/bootstrap/dist/css/bootstrap.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <div class="container" >
        <div class="spacer">
            <div class="row ">
                <div class="col-lg-4 col-lg-offset-4">
                    <input type="search" class="form-control" placeholder="Search for episode" />
                </div>
            </div>
            <div class="row spacer">
                <div class="col-lg-4 col-lg-offset-4">
                    <table width="100%">
                        <thead>
                            <tr>
                                <th>Title</th>
                                <th>Link</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr">
                                <td>Sample episode 1</td>
                                <td>
                                    <a href="#">View</a>
                                </td>
                            </tr>
                            <tr">
                                <td>Sample episode 2</td>
                                <td>
                                    <a href="#">View</a>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</body>
</html>
  

This gets us to this point:

Step 1 of 'Thinking in React' - Break the UI into a component hierarchy

We have a look at the UI and identify the following components:

Step 2: Build a static version in React

We build all the required components to render the view and bind them to some data

index.html
  
<!DOCTYPE html>
<html>
    <head>
        <script src="bower_components/react/react.js"></script>
        <script src="bower_components/react/JSXTransformer.js"></script>
        <link  href="bower_components/bootstrap/dist/css/bootstrap.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <div id="container"></div>


        <script type="text/jsx">
        /** @jsx React.DOM **/

        var EpisodeRow = React.createClass({
            render: function() {
                return (
                    <tr>
                        <td>{this.props.episode.title}</td>
                        <td><a href="#">view</a></td>
                    </tr>
                );
            }
        });

        var EpisodeTable = React.createClass({
            render: function() {
              var props = this.props;
              var rows = props.episodes
                .map(function(episode){
                  return <EpisodeRow key={episode.title} episode={episode} />;
                });


              return (
                  <div className="row spacer">
                    <div className="col-lg-4 col-lg-offset-4">
                      <table width="100%">
                          <thead>
                              <tr>
                                  <th>Title</th>
                                  <th>Link</th>
                              </tr>
                          </thead>
                          <tbody>{rows}</tbody>
                      </table>
                    </div>
                  </div>
              );
            }
        });

        var SearchBar = React.createClass({
            render: function() {
                return (
                    <div className="row ">
                      <div className="col-lg-4 col-lg-offset-4">
                          <input type="search" className="form-control" placeholder="Search for episode" />
                      </div>
                    </div>
                );
            }
        });

        var FilterableEpisodeTable = React.createClass({
            render: function() {
                return (
                    <div className="spacer">
                        <SearchBar   />
                        <EpisodeTable episodes={this.props.episodes} />
                    </div>
                );
            }
        });


        var episodes = [{
              title : "Angular with Yeoman",
          },{
              title : "Using D3 with Rickshaw and Angular",
          },{
              title : "Canvas with paper.js",
          },{
              title : "Express.js middleware",
          },{
              title : "MEAN stack - episode 1",
          }
        ];

        React.renderComponent(<FilterableEpisodeTable episodes={episodes} />, document.getElementById('container'));

        </script>
    </body>
</html>
  

Step 3: Identify the minimal (but complete) representation of UI state

We identify that in our application, the search text is the only state

Step 4: Identify where your state should live

We identify a good place for the state to live, the FilterableEpisodeList

Step 5: Add inverse data flow

We hook up all the controls and let the state flow correctly through the component hierarchy.

index.html
  

<!DOCTYPE html>
<html>
    <head>
        <script src="bower_components/react/react.js"></script>
        <script src="bower_components/react/JSXTransformer.js"></script>
        <link  href="bower_components/bootstrap/dist/css/bootstrap.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <div id="container"></div>


        <script type="text/jsx">
        /** @jsx React.DOM */

        var EpisodeRow = React.createClass({
            getInitialState: function() {
                return {
                    viewed: false
                };
            },
            handleClick: function(){
              this.setState({viewed: true});
            },
            render: function() {
                return (
                    <tr>
                        <td>{this.props.episode.title}</td>
                        <td><a  onClick={this.handleClick}>view {this.state.viewed ? '(viewed)' : ''}</a></td>
                    </tr>
                );
            }
        });

        var EpisodeTable = React.createClass({
            render: function() {
              var props = this.props;
              var rows = props.episodes
                .filter(function(episode){
                  return episode.title.toLowerCase().indexOf(props.filterText.toLowerCase()) > -1;
                })
                .map(function(episode){
                  return <EpisodeRow key={episode.title} episode={episode} />;
                });


              return (
                  <div className="row spacer">
                    <div className="col-lg-4 col-lg-offset-4">
                      <table width="100%">
                          <thead>
                              <tr>
                                  <th>Title</th>
                                  <th>Link</th>
                              </tr>
                          </thead>
                          <tbody>{rows}</tbody>
                      </table>
                    </div>
                  </div>
              );
            }
        });

        var SearchBar = React.createClass({
            handleChange: function() {
                this.props.onUserInput(
                    this.refs.filterTextInput.getDOMNode().value
                );
            },
            render: function() {
                return (
                    <div className="row ">
                      <div className="col-lg-4 col-lg-offset-4">
                        <form onSubmit={this.handleSubmit}>
                          <input ref="filterTextInput" value={this.props.filterText} onChange={this.handleChange} type="search" className="form-control" placeholder="Search for episode" />
                        </form>
                      </div>
                    </div>
                );
            }
        });

        var FilterableEpisodeTable = React.createClass({
            getInitialState: function() {
                return {
                    filterText: ''
                };
            },

            handleUserInput: function(filterText) {
                this.setState({
                    filterText: filterText
                });
            },

            render: function() {
                return (
                    <div className="spacer">
                        <SearchBar onUserInput={this.handleUserInput} filterText={this.state.filterText} />
                        <EpisodeTable filterText={this.state.filterText} episodes={this.props.episodes} />
                    </div>
                );
            }
        });

        var episodes = [{
              title : "Angular with Yeoman",
          },{
              title : "Using D3 with Rickshaw and Angular",
          },{
              title : "Canvas with paper.js",
          },{
              title : "Express.js middleware",
          }
        ];

        React.renderComponent(<FilterableEpisodeTable episodes={episodes} />, document.getElementById('container'));

        </script>
    </body>
</html>
  

Wrap up

I wish more frameworks would package their philosophy so well for people to understand and adopt. Have a look at the video if you haven't yet, it explains all the steps in detail.