Not so long ago, React 0.14 was released. You can read the full guide on the facebook’s github, but it’s very long and detailed. If you want TL;DR, in addition with an example, read this article.

I will do Warning Driven Development on this public gist.

Legacy

So this is the code (after a few restrictions of my eslint):

    var React = require("react");

    var allItems = [];
    allItems.push("Buy ingredients for Crock Pot");
    allItems.push("Pick up chair at IKEA");
    allItems.push("Go see mom");

    class TodoList extends React.Component {
      constructor(props){
        super(props);
        this.addEvent = this.addEvent.bind(this);
      }
      getInitialState() {
        return { allItems };
      }
      render() {
        var items = this.props.items.map((item) => {
          return <li><TodoItem item={item} /></li>;
        });
        return (
          <div>
            <ul>{items}</ul>
            <p><NewTodoItem addEvent={this.addEvent} /></p>
          </div>
        );
      }
      addEvent(todoItem){
        allItems.push(todoItem.newItem);
        this.setState({ allItems });
      }
    }

    class TodoItem extends React.Component {
      render(){
        return <div>{this.props.item}</div>;
      }
    }

    class NewTodoItem extends React.Component {
      constructor(props){
        super(props);
        this.onSubmit = this.onSubmit.bind(this);
      }
      componentDidMount(){
        React.findDOMNode(this.refs.itemName).focus();
      }
      render(){
        return (<form onSubmit={this.onSubmit}>
          <input ref="itemName" type="text" />
        </form>);
      }
      onSubmit(event){
        event.preventDefault();
        var input = React.findDOMNode(this.refs.itemName);
        var newItem = input.value;
        this.props.addEvent({ newItem });
        input.value = '';
      }
    }

    React.render(<TodoList items={allItems} />, APP_ROOT);

This is the console output with old React 0.13:

Two warnings. And the code seems to work. Cool.

Upgrading React

In order to get the newest React, I had to run npm install --save react react-dom. And this is where we have to stop, because these are two packages to install. Splitting the react package into two packages is the first change of 0.14 to meet - read more here.

What does the console tell after the upgrade?

5 warnings, all colored red. Not cool.

In fact, 2nd and 3rd warning are the same as before, only the color changed. The 1st and 5th warnings tell about being deprecated. The 4th warning tells about the composition.

The good news is - the code still works. This is the policy of React - if your code is warningless, you are guaranteed to upgrade with no errors. In our example we had 2 warnings, but the concern must have been not very crucial.

Let’s get rid of the warnings one by one.

Warning: React.render is deprecated

As mentioned before, the DOM-related parts of React now make a separate package react-dom. We simply require it…

    var ReactDOM = require("react-dom");

…and then call render from it, rather than from react:

    // React.render(<TodoList items={allItems} />, APP_ROOT);
    ReactDOM.render(<TodoList items={allItems} />, APP_ROOT);

Voilà!

Warning: React.findDOMNode is deprecated

Because we already imported react-dom, this one can be fixed right away. We use findDOMNode in two lines, so let’s change the package also there:

    // React.findDOMNode(this.refs.itemName).focus();
    ReactDOM.findDOMNode(this.refs.itemName).focus();
    // var input = React.findDOMNode(this.refs.itemName);
    var input = ReactDOM.findDOMNode(this.refs.itemName);

We got rid of the warning, but interestingly enough - we can remove findDOMNode completely from the code. This is because React 0.14 changes the interpretation of refs (only for built-in DOM components).

    // ReactDOM.findDOMNode(this.refs.itemName).focus();
    this.refs.itemName.focus();
    // var input = ReactDOM.findDOMNode(this.refs.itemName);
    var input = this.refs.itemName;

Warning: getInitialState was defined on TodoList, a plain JavaScript class

You might have already noticed that getInitialState is a dead code. What’s more, the TodoList component renders a TodoItem per item in this.props.items, which is a property. The TodoList component doesn’t use its state at all.

This kind of property “imitating” the state is a misusage of React and is considered as an anti-pattern. However, I will leave it now since it’s not the concern of this post.

To make the warning disappear without changing the behavior - simply remove getInitialState method from TodoList.

Warning: Each child in an array or iterator should have a unique “key” prop

If you still don’t know why key prop should be set, see one of our earlier posts.

As the rest of the warning message prompts: Check the render method of TodoList. We need every <li> tag to have unique key property. This is easy to achieve, because we already use map method. map’s callback can take another argument being a unique index of each item. So this is how it looks like after the fix:

    // var items = this.props.items.map((item) => {
    //  return <li><TodoItem item={item} /></li>;
    // });
    var items = this.props.items.map((item, index) => {
      return <li key={index}><TodoItem item={item} /></li>;
    });

Warning: validateDOMNesting(…): <form> cannot appear as a descendant of <p>

This one is interesting. Apparently, React 0.14 introduced some new rules of composing the HTML tags. But I don’t see any sign of it on the facebook’s changelog.

We can simply remove the wrapping <p> tag around NewTodoItem component:

    // <p><NewTodoItem addEvent={this.addEvent} /></p>
    <NewTodoItem addEvent={this.addEvent} />

That’s it. The code works the same, but causes no warnings!

Stateless functional components

We can go a little further and try a new feature of React 0.14 - stateless functional components.

The TodoItem component is stateless, so let’s rewrite it:

    // class TodoItem extends React.Component {
    //   render(){
    //     return <div>{this.props.item}</div>;
    //   }
    // }
    var TodoItem = (props) => {
      return <div>{props.item}</div>;
    };

Summary

React seems to care very much about its users when it comes to upgrade. The warning messages are often profound and contain links for more information. Additionally, you are not even forced to upgrade your code immediately. This makes the migrations very easy.

comments powered by Disqus