06 September 2016

Written by Andrea Rosati

Introduction

The initial purpose of the blog was to show how ReactJS is used inside the Forms for Confluence Connect plugin, but later it changed to mainly share how I approached the new (for me) library. I think would be a good idea to write about my experience using React, the problems I found and provide some tips for people that are going to start using it. I'll try to summarize all things that I wish I'd known when I started out, or things that really helped me to use React.

What is React?

From the React documentation it is described as a JavaScript library for creating user interfaces. Many people choose to think of React as the V in MVC. What I came to understand while working with it is that React is essentially a library to render HTML. That's all React outputs, HTML. That means that while using React to organize your HTML, it's still possible to use other powerful tools like jQuery and ES6 that are used in the Forms for Confluence Connect plugin.

Thinking in React

My initial dumb suggestion to approach React is to read the first steps in the documentation. The most useful thing I found is Thinking in React. The main topic to wrap your head around React is to start thinking in components. As explained by the documentation the first step is to break the UI into a component hierarchy and to render each component separately. Here's a very simple example of a table with two rows.


class Table extends React.Component {
  render() {
    return <div>
      <h2>Table title</h2>
      <table>
        <thead>
        <Header />
        </thead>
        <tbody>
        <Row firstname="Jill" lastname="Smith" age="50"/>
        <Row firstname="Eve" lastname="Jackson" age="94"/>
        </tbody>
      </table>
    </div>
  }
}

class Header extends React.Component {
  render() {
    return <tr>
      <th>Firstname</th>
      <th>Lastname</th>
      <th>Age</th>
    </tr>
  }
}

class Row extends React.Component {
  render() {
    return <tr>
      <td>{this.props.firstname}</td>
      <td>{this.props.lastname}</td>
      <td>{this.props.age}</td>
    </tr>
  }
}

The example highlights that it is possible to mix React components with regular HTML elements in your render function. It's good practice to define simple, single responsibility components.

The render function in our component is used to display the HTML content, but you are only permitted to return one DOM element or React component. If you need to display more html elements, like in the case of Table component, they need to be wrapped by a div.

State vs Props

This is the heart of React. Each component has a state and properties. When those change, the render method is called with the new values and the UI is updated to the new output. It is basically the step to pass from a static rendering engine to make React components dynamic.

The properties are read-only by their owning component. Any component can change its own state or even a different component's state if it wants to. A state should change following a user action, such as a state value that keeps track of whether or not the user has clicked on a button.

I suggest that you don't use the state and the properties when they're not needed, and it's preferable to avoid them where possible. This means that is preferable to have "stateless component", which is a component that will always do the same thing when given the same set of properties. Stateless components are easier to understand and easier to test, which is always a good thing.


class Table extends React.Component {

  parentClickButtonFunction(event) {
    this.setState({
      tableTitle: "new title"
    });
  }

  constructor() {
    super();
    this.state = {
      tableTitle: "initial table title"
    }
    this.parentClickButtonFunction = this.parentClickButtonFunction.bind(this);
  }

  render() {
    return <div>
      <h2>{this.state.tableTitle}</h2>
      <table>
        <thead>
        <Header />
        </thead>
        <tbody>
        <Row firstname="Jill" lastname="Smith" age="50"/>
        <Row firstname="Eve" lastname="Jackson" age="94"/>
        </tbody>
      </table>
      <AddRowButton parentClickButtonFunction={this.parentClickButtonFunction}/>
    </div>
  }
}

class Header extends React.Component {
  render() {
    return <tr>
      <th>Firstname</th>
      <th>Lastname</th>
      <th>Age</th>
    </tr>
  }
}

class Row extends React.Component {
  render() {
    return <tr>
      <td>{this.props.firstname}</td>
      <td>{this.props.lastname}</td>
      <td>{this.props.age}</td>
    </tr>
  }
}

class AddRowButton extends React.Component {

  clickButtonFunction(event) {
    this.props.parentClickButtonFunction();
  }

  render() {
    return <button type="button" onClick={this.clickButtonFunction.bind(this)}>Click Me!
    </button>
  }
}

Now I would like to focus on how the state and properties work. I advise that you give the responsibility of the state to the parent component and propagate the changes from the top to the bottom of the components hierarchy using the properties.

The button in the example above wants to change the state, but to do that, it actually propagates the call to its parent using the callback function 'parentClickButtonFunction' in its properties.

ES6 Classes and main methods

In the Forms for Confluence Connect plugin we use ES6 class syntax to define components. The important things to keep in mind are that any new component should extend 'React.Component' and that there are some important methods to be mindful of.

  • render:

    The render function in our component is used to display the HTML output of your component.

  • constructor:

    The constructor is where you initialize the component state and properties

  • componentDidMount:

    Invoked once, only on the client (not on the server), immediately after the initial rendering occurs. At this point in the lifecycle, you can access any refs to your children (e.g., to access the underlying DOM representation). The componentDidMount() method of child components is invoked before that of parent components.

No Autobinding

As reported in the documentation, ES6 classes don't automatically bind this to the instance and it's recommended to bind the event handlers in the constructor so that they are only bound once for every instance. That explains the previous Table constructor:

constructor() {
    super();
    this.state = {
      tableTitle: "initial table title"
    }
    this.parentClickButtonFunction = this.parentClickButtonFunction.bind(this);
}

Conclusion

Thinking in React is thinking in a different way compared to how I, and probably the majority of people, were used to. It can be difficult at the beginning to understanding how properties, state, and component communication work because it is not straightforward. The good part I found is that you can always tell how your component will render by looking at one source file. This may be the most important benefit. If you know the state, you know the rendered output. You don't have to trace program flow. When working on complex applications, especially in teams, this is critically important.



blog comments powered by Disqus