React Component Patterns

By Gustavo Matheus

As React became more and more popular in front-end development, various design patterns and new concepts emerged. This article aims to summarize some common design patterns in React development.

Stateful vs stateless: Ateful.

The React component can be stateful and can manipulate and change its internal state during its lifetime. The React component can also be stateless, accepting only props passed in from the parent component and presenting it.

Here is a stateless Button component whose behavior is entirely determined by the props passed in:

const Button = props => 
  <button onClick={props.onClick}>
    {props.text}
  </button>
Copy the code

Here is a stateful component (using the stateless component described above):

class ButtonCounter extends React.Component {
  constructor() {
    super(a);this.state = { clicks: 0 };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState({ clicks: + +this.state.clicks });
  }

  render() {
    return (
      <Button
        onClick={this.handleClick}
        text={`You've clicked meThe ${this.state.clicks} times! `} / >)}}Copy the code

As you can see, the ButtonCounter component maintains its own state in state, whereas the Button component just renders to props. This difference may seem small, but the stateless Button component is highly reusable.

Container vs Presentational components

When interacting with external data, we can classify components into two categories:

  • Container components: mainly responsible for the interaction (communication) with external data, such as data binding with Redux etc.
  • Presentation component: Render according to its own state and props received from the parent component, without communicating directly with external data sources.

Let’s look at a presentation component:

const UserList = props =>
  <ul>
    {props.users.map(u => (
      <li>{u.name} - {u.age} years old</li>
    ))}
  </ul>
Copy the code

The presentation component can be updated by a container component:

class UserListContainer extends React.Component {
  constructor() {
    super(a)this.state = { users: [] }
  }

  componentDidMount() {
    fetchUsers(users= > this.setState({ users }));
  }

  render() {
    return <UserList users={this.state.users} />}}Copy the code

Data acquisition and rendering are separated by separating components into container components and presentation components. This also makes UserList reusable. If you want to learn more, here are some very good articles that explain it very clearly.

Higher-order components

HOC comes in handy when you want to reuse a component’s logic. Higher-order components are JavaScript functions that take the React component as an argument and return a new component.

An example would be to write a menu component that, when clicked on a menu item, expands the current menu item to display submenus. Of course we can control the state of the menu component in the parent component, but a more elegant way is to use higher-order components:

function makeToggleable(Clickable) {
  return class extends React.Component {
    constructor() {
      super(a);this.toggle = this.toggle.bind(this);
      this.state = { show: false };
    }

    toggle() {
      this.setState({ show:!this.state.show });
    }

    render() {
      return (
        <div>
          <Clickable
            {. this.props}
            onClick={this.toggle}
          />
          {this.state.show && this.props.children}
        </div>); }}}Copy the code

In this way, we can use JavaScript’s decorator syntax to apply our logic to the ToggleableMenu component:

@makeToggleable
class ToggleableMenu extends React.Component {
  render() {
    return (
      <div onClick={this.props.onClick}>
        <h1>{this.props.title}</h1>
      </div>); }}Copy the code

Now we can put any submenu contents into the ToggleableMenu component:

class Menu extends React.Component {
  render() {
    return (
      <div>
        <ToggleableMenu title="First Menu">
          <p>Some content</p>
        </ToggleableMenu>
        <ToggleableMenu title="Second Menu">
          <p>Another content</p>
        </ToggleableMenu>
        <ToggleableMenu title="Third Menu">
          <p>More content</p>
        </ToggleableMenu>
      </div>); }}Copy the code

When you use Redux’s Connect or React Router’s withRouter function, you’re using higher-order components!

Render Callbacks

In addition to the higher-order components described above, render callbacks are another design pattern for making components reusable. The core of the render callback is the children (or children, also known as props. Children) that the Component receives, which are not provided as the React Component but as callback functions. Using the HOC component above as an example, we rewrite it with a render callback as follows:

class Toggleable extends React.Component {
  constructor() {
    super(a);this.toggle = this.toggle.bind(this);
    this.state = { show: false }
  }

  toggle() {
    this.setState({ show:!this.state.show });
  }

  render() {
    return this.props.children(this.state.show, this.toggle)
  }
}
Copy the code

Now we can pass the callback function to the Toggleable component as a child. We implemented the previous HOC component ToggleableMenu in a new way:

const ToggleableMenu = props= > (
  <Toggleable>
    {(show, onClick) => (
      <div>
        <div onClick={onClick}>
          <h1>{props.title}</h1>
        </div>
        { show && props.children }
      </div>
    )}
  </Toggleable>
)
Copy the code

And our new Menu component implementation is as follows:

class Menu extends React.Component {
  render() {
    return (
      <div>
        <ToggleableMenu title="First Menu">
          <p>Some content</p>
        </ToggleableMenu>
        <ToggleableMenu title="Second Menu">
          <p>Another content</p>
        </ToggleableMenu>
        <ToggleableMenu title="Third Menu">
          <p>More content</p>
        </ToggleableMenu>
      </div>); }}Copy the code

Yes, you read that right, the new Menu component is exactly the same as the previous implementation in HOC mode!

In this implementation, we separate the state inside the component from the rendering logic of the component. In the example above, we put the render logic in ToggleableMenu’s render callback, while the state of the presentation component is still maintained within the Toggleable component.

To learn more

The examples above are just the basics of the React design pattern. If you want to learn more about React design patterns, here are some great learning materials that are worth checking out:

  • React Component Patterns by Michael Chan
  • React Patterns
  • Presentational and Container Components
  • React Higher Order Components in depth
  • Function as Child Components
  • Recompose
  • Downshift

Pay attention to wechat public number: KnownsecFED, code to get more quality dry goods!