My React Best Practices

最近、a-blog cmsの一部機能のUI改善にReact導入を進めている。採用した理由としては、Reactを使えば仕様変更や機能追加に強いUIの設計が可能ということ。よく使われるコンポーネントを汎用的に作っておけばそのコンポーネントを機能的に向上させた際に、他の場所で同じコンポーネントが使われていたとしても、stateとpropsの状態でそれ通りの動きを担保してくれる。

ただ、Reactを使う際はちゃんと設計を考えないとメンテナンス性のいいコードにもなれば非常に醜いコードになり得たりもする。
そこで今回は、これまで培ったMy React Best Practiceをメモ代わりにここに載せていく。新しい発見があれば今後も更新予定。

  1. コンポーネント内では必要最低限のStateを保持
  2. 公開されているライブラリはなるべくHOCライブラリを探すようにする
  3. propsリレーをコンテナー側からの注入で解決する
  4. StateをStoreで管理するか、ローカルのコンポーネントで管理するか

1. コンポーネント内では必要最低限のStateを保持

例えばnumberという状態を保持する際renderメソッド内ではnumberの状態に応じてテキストの色を変えたい場合がある。そういった場合にnumberとは別にcolorをいう状態をnumberの値に応じて保持してしまうのは非常にナンセンス。renderメソッド内で関数を呼び出してその都度色を取得するようにしよう。

class MyComponent extends component {
  constructor(props) {
    super(props);
    this.state = {
        number: 0
    };
  }

  onClick(e) {
    const number = e.target.value;
    this.setState({number});
  }

  getColor() {
    const {number} = this.state;
    switch(number) {
        case 0:
            return 'red';
        case 1:
            return 'white';
        case 2:
            return 'yellow';
        case 3:
            return 'green';
        default:
            return 'black';
    }
  }
  
  render() {
    const color = this.getColor();
    const number = this.state.number;
    return (<div>
        <span style=>{number}</span>
        <button onClick={this.onClick.bind(this)} value="0">0</button>
        <button onClick={this.onClick.bind(this)} value="1">1</button>
        <button onClick={this.onClick.bind(this)} value="2">2</button>
        <button onClick={this.onClick.bind(this)} value="3">3</button>
    </div>);
  }
}

2. 公開されているライブラリはなるべくHOCライブラリを探すようにする

ある要素同士のソートやドラッグアンドドロップなど普通に実装しようと思えば少し難しい処理などがある。その処理を実現するためにnpmなどでReactコンポーネントを探さないといけない時があるかもしれない。その際にそのコンポーネントがHOC(High Order Component)かどうかはかなり重要だ。High Order Componentとはあるコンポーネントを代入してラップするためのコンポーネント。例えば下はReact Sortable (HOC)というライブラリの例。

import React, {Component} from 'react';
import {render} from 'react-dom';
import {SortableContainer, SortableElement, arrayMove} from 'react-sortable-hoc';

const SortableItem = SortableElement(({value}) =>
  <li>{value}</li>
);

const SortableList = SortableContainer(({items}) => {
  return (
    <ul>
      {items.map((value, index) => (
        <SortableItem key={`item-${index}`} index={index} value={value} />
      ))}
    </ul>
  );
});

class SortableComponent extends Component {
  state = {
    items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'],
  };
  onSortEnd = ({oldIndex, newIndex}) => {
    this.setState({
      items: arrayMove(this.state.items, oldIndex, newIndex),
    });
  };
  render() {
    return <SortableList items={this.state.items} onSortEnd={this.onSortEnd} />;
  }
}

render(<SortableComponent/>, document.getElementById('root'));

コンポーネントをドラッグアンドドロップでソートする機能を提供するコンポーネントだが、HOCになっていることで自分でソートする中身のリストアイテム(li)のHTMLを定義できたり、そのリスト自体(ul)を定義できたりする。つまりHOCとは、コンポーネントそれ自体ではなくコンポーネントにある機能を付加するための関数だ。こういったHOCなライブラリを選択することで、後々コンポーネントにさらにいろいろな機能を付け加える際に余計な制約に縛られることがなくなる。

3. propsリレーをコンテナー側からの注入で解決する

これはReduxなどを使っている際や、いくつもコンポーネントをネストしている際に便利になってくる話。例えば、コンポーネントをいくつもネストしているとpropsリレーが発生して、いくつもファイルを修正しないといけない状況はよくある。実を言うとcontainer側で最初からすべての引数を決定できる。

class Container extends Component {
    ....
  render() {
    const { items, news, token } = this.props;
    return (
        <div>
            <ParentComponent items={items} 
                child={<Child news={news} />}
             />
        </div>
    );
  }
}
class ParentComponent extends Component {
   ...
  render() {
    const {child} = this.props;
    return (
       <div>
        <p>my child is</p>
        {child}
       </div>
    );
  }
}

つまりContainerからコンポーネントを注入しておけばpropsとしてコンポーネント自体を取得できるというわけ

4. StateをStoreで管理するか、ローカルのコンポーネントで管理するか

Reduxを導入するのはいいが、Storeにすべてのコンポーネントのstateを持たせてしまうのはよくない。コンポーネント単体で解決する問題はコンポーネント内だけのローカルstateで解決すべきだ。少しコンポーネントの色を変更したいだけの時などにReduxを使ってしまうと、それに付随するactionやreducer、constantなどを変更することになり見通しも悪くなるし、いろいろとめんどくさい。ReduxのStoreはグローバル変数のようなもの。最終手段としてactionの発行を考えたほうがいい。


堀 悟大

アップルップル フロントエンドエンジニア。2014年高知大学理学部卒業。学生時代にHTML5のCanvas要素を使ってゲームを作っていたことでWeb全般に興味をもつ。アップルップル入社後はa-blog cmsを便利に使うための機能の実装や、HTML5の技術を使ったデジタルサイネージの実装を行う。趣味は英語。読むことも話すことも好き。

Home
Next entry →