React Design Patterns


Hi everyone, here is not getting started guide for writing React. You should have some basic of React such as basic concept of React and practical React tutorial.

Overview

I’m follwing Airbnb styleguide and using react in pattern books for references https://github.com/krasimir/react-in-patterns, https://github.com/vasanthk/react-bits

Designing React Web Application

Nowaday, we have many appoaches to design the composition of react component.

Here is one of approaches to design

Preparation

  1. Use Airbnb Styleguide for consistency code style
  2. Use popular React boilerplate (create-react-app) for reducing complexiity to manage build tools such as webpack, babel, etc.

Basic Design

In React documentation describes Thinking in React for explaining how to design react component and how they compose each others. However, when I ‘ve getting started with React, I realize the component design thinking is most important thing for designing react application. In a React application, it will be at least one component, or can be divived into child component which should work with the parent one.

  1. Start with a few React component for reducing the complexity in state management.
  2. Break the code down to component when necessary, if it feel easier.
  3. The component can break into 2 parts: Container Component and Presentional Component, when necessary.
    1. Container Component
    2. Presentational Component

Advanced Design: Reusable Components

  1. Break the code down to component if you want to reuse the components.
  2. Use High-order Component when necessary.

Composing components

  1. Passing a child as a prop
  2. Passing state through the props
  3. Using top component for storing state
  4. Using simple state management library for small application
    1. Honorable mention libraries: pure-store, unstated and unistoreother approches are shown at React State Musuem which good place for reviewing react state management library.
    2. I’m interesting in mobx approaches for small application
  5. Using Redux , mobx for big application

Alternative, you can use MVC approach if you familiar, however, in my opinion using MVC on react.

Design Patterns and Techniques

Component Organization Templates & Patterns

Full Feature (Stateful Component)

class Person extends React.Component {
  constructor (props) {
    super(props);

    this.state = { smiling: false };

    this.handleClick = () => {
      this.setState({smiling: !this.state.smiling});
    };
  }

  componentWillMount () {
    // add event listeners (Flux Store, WebSocket, document, etc.)
  }

  componentDidMount () {
    // React.getDOMNode()
  }

  componentWillUnmount () {
    // remove event listeners (Flux Store, WebSocket, document, etc.)
  }

  get smilingMessage () {
    return (this.state.smiling) ? "is smiling" : "";
  }

  render () {
    return (
      <div onClick={this.handleClick}>
        {this.props.name} {this.smilingMessage}
      </div>
    );
  }
}

Person.defaultProps = {
  name: 'Guest'
};

Person.propTypes = {
  name: React.PropTypes.string
};

Ref: https://github.com/chantastic/react-patterns#component-organization

Stateless Component

function StatelessComponent(){
  return (
    <div>
      This is Stateless Component.
    </div>
  )
};

Stateless Component with props

function StatelessComponent({ data }){
  return (
    <div>
      {data}
    </div>
  )
};

Container Component

We can split our component into 2 types: Container component, Presentational component

// CommentList.js

class CommentList extends React.Component {
  render() {
    return (
      <ul>
        {this.props.comments.map(({body, author}) => {
          return <li>{body}{author}</li>;
        })}
      </ul>
    );
  }
}
// CommentListContainer.js

class CommentListContainer extends React.Component {
  getInitialState () {
    return { comments: [] }
  }

  componentDidMount () {
    $.ajax({
      url: "/my-comments.json",
      dataType: 'json',
      success: function(comments) {
        this.setState({comments: comments});
      }.bind(this)
    });
  }

  render () {
    return <CommentList comments={this.state.comments} />;
  }
}

Read more for Presentational and container components, using ref https://github.com/chantastic/react-patterns#container-components

JSX Rendering

if

{isActive && <p>Message</p>}

if-else

{
  isTrue ? (
    <span>Rendered when `TRUE`</span>
  ) : (
    <span>Rendered when `FALSE`</span>
  );
}

Loop items for JSX

const todos = [
    {id: 1, text: 'Test'},
    {id: 1, text: 'Write a paper'}
]
function sampleComponent(){
  return (
      <ul>
        {todos.map(todo => (
        	<li key={todo.id}>{todo.text}</li>
        ))}
      </ul>
  )
};

Two way binding

class App extends React.Component {
    
  state = {
    textbox: ""
  }
  render() {
    return (
      <div>
        <input
          type="text"
          value={this.state.textbox}
          onChange={(e) => this.setState({ textbox: e.target.value })} />
        {this.state.textbox}
      </div>
    );
  }

}

Composing components

File Structure

Todo Components

├── components
│   ├── todo
│      ├── index.jsx
│      ├── AddTodo.jsx
│      └── TodoList
│          ├── index.jsx
│          └── Todo.jsx
├── app.js
└── index.js

Passing a child as a prop

Every React component has children props.

const Title = function () {
  return <h1>Hello there!</h1>;
}
const Header = function ({ title, children }) {
  return (
    <header>
      { title }
      { children }
    </header>
  );
}
function App() {
  return (
    <Header title={ <Title /> }>
      Resting content
    </Header>
  );
};

ref: https://krasimir.gitbooks.io/react-in-patterns/content/chapter-04/#passing-a-child-as-a-prop

Passing state through the props

// Counter.jsx
class Counter extends React.Component {
  state = {
    count: 0
  }
  
  handleAdd(){
    this.setState({ count: this.state.count + 1});
  }

  render() {
    return (
      <CounterView 
        value={this.state.count}
        onAdd={() => this.handleAdd()}>
      </CounterView>
    );
  }
}

// CounterView.jsx
function CounterView({ value, onAdd }) {
  return (
    <React.Fragment>
      <div>Count: {value}</div>
      <button onClick={onAdd}>Add</button>
    </React.Fragment>
  );
};

Using top component for storing state

// store.js
export default {
  root: {},
  getRoot() {
    return this.root;
  },
  setRoot(value) {
    this.root = value;
  }
}

// Counter.jsx
class Counter extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      count: 0
    }
    store.setRoot(this);
  }
  
  handleAdd(){
    this.setState({ count: this.state.count + 1});
  }

  render() {
    return (
      <CounterView 
        value={this.state.count}></CounterView>
    );
  }
}

// CounterView.jsx
function CounterView({ value }) {
  return (
    <React.Fragment>
      <div>Count: {value}</div>
      <button onClick={() => store.getRoot().handleAdd()}>Add</button>
    </React.Fragment>
  );
};

Thanks for Anas

Share state between components

Design choice:

  1. Small Application: pure-store, unstated and unistore
  2. Small - Medium Application: mobx (Also using unstated and unistore )
  3. Using Redux , mobx for big application

Read more how to implement in each state management library in React State Musuem

How to choose the composing component pattern?

Style Guide & Naming

Style Guide

Read more in Airbnb’s Style guide

Naming Events

class Owner extends React.Component {
  handleClick () {
    // handle click event
  }

  render () {
    return <div onClick={() => this.handleClick()}></div>;
  }
}

Original: https://github.com/chantastic/react-patterns#naming-events


Other Resources

Other topics can read in below resources:

State

P.S. PR, Suggestions are welcome



Comment