Follow me if you want to grow your full-stack JavaScript skills with my screencasts and courses.

Thinking in react

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:

  • EpisodeRow
  • EpisodeTable
  • SearchBar
  • FilterableEpisodeTable - contains the other 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.