maesblog

【React】コンポーネントをシンプルに書くためのStateless Functional Componentsについて

React v0.14がリリースされてから半年近くが過ぎました。自分としてはやっとReact v0.14で追加された新しい機能についても細かいところまで気にかける余裕が出てきたかなといったところです(v15.0のリリースが差し迫っていますが…)。そんなReact v0.14で新しく追加された機能の中で、最近面白いなと思った「Stateless functional components(SFC: 状態を持たない関数コンポーネント)」について今回は書いてみようと思います。

Stateless functional componentsとは

Stateless functional components(SFC / Stateless function components)は、React v0.14で新たに追加されたシンプルに書けるコンポーネントとなります。Reactを書いていると、共通ヘッダーやフッターとして使うようなコンポーネントなど機能にはほとんど関係なく状態を持たない(ステートレスな)シンプルなコンポーネントを書くことも多々あるかと思います。

こうしたシンプルなコンポーネントを書くための新しい構文として、Stateless functional componentsがReact v0.14で追加されました。Stateless functional componentsは、その名の通り関数で書くコンポーネントで、引数でpropsを受け取り、戻り値としてレンダリングしたい要素(DOM)を返すだけのものとなっています。

デザイナーにも優しい構文

Reactの場合、テンプレートの部分もすべてJavaScriptファイル内に書くため、デザイナーで特にJavaScriptを書かない方には、難しく思われがちです。

基本構文

ただ、このStateless functional componentsはその名の通り、JavaScriptの関数を使って以下のように簡単にコンポーネントを書けるようになっています。JavaScriptが苦手な方も、書き方さえ覚えてしまえばさほど恐れる心配はありません。簡単に言えば「DOMを関数の戻り値に書く」だけです。

function HelloMessage(props) {
  return <div>Hello {props.text}</div>;
}

ReactDOM.render(
  <HelloMessage text="World" />,
  document.querySelector('content')
);
Stateless functional componentsの構文

Stateless functional componentsを書くポイントは以下の通りです。

  • 関数名がコンポーネント名になる
  • 関数名の頭文字を大文字にする
  • return値にJSX(DOM)を書く
  • 関数の引数でpropsを受け取る

上記を通常のReact.createClass()を使ったコンポーネントの書き方で書くと以下のようになります。オブジェクトリテラル形式で書いたり、render()メソッドを使ったりだとか、Stateless functional componentsと比べると若干複雑であることがわかります。

var HelloMessage = React.createClass({
  render: function () {
    return (
      <div>Hello {this.props.text}</div>
    );
  }
});
一般的なReact.createClass()で書いたComponentの構文

ES6のAllow Functionで書くと

ちなみに、Stateless functional componentsはES6(ES2015)のアローファンクションを使って書くこともできます。以下のようによりシンプルに書くことができます。

const HelloMessage = (props) => <div>Hello {props.text}</div>;

ReactDOM.render(
  <HelloMessage text="World" />,
  document.querySelector('content')
);
ES6のAllow Functionを使ってStateless functional componentsを書く場合

JSXを複数行で書く場合は、以下のようにも書けます。

const HelloMessage = (props) => (
  <div>
    Hello {props.text}
  </div>
);
ES6のAllow Functionを使ってStateless functional componentsを書く場合

変数などを指定する場合は、以下のように書きます。

const HelloMessage = (props) => {
  let text = props.text;
  return (
    <div>
      Hello {text}
    </div>
  );
};
ES6のAllow Functionを使ってStateless functional componentsを書く場合

ファイルを分割して書く場合(エクスポート)

ファイルを分割して書く場合は、他のファイルで読み込めるように関数を以下のようにES6のexportを使ってエクスポートしておく必要があります。

import React from 'react';

function HelloMessage(props) {
  return <div>Hello {props.text}</div>;
}

export default HelloMessage;
HelloMessageをエクスポート

Stateless functional componentsのプロパティ

Stateless functional componentsは、その中で状態を持たないため、通常のComponentで使えるcomponentWillMount()メソッドのようなライフサイクルメソッドを持っていません。Stateless functional componentsが持つのは以下の2つのプロパティのみとなります。

  • .propTypes: コンポーネントに渡されたpropsをvalidateします。
  • .defaultProps: propsのデフォルトの値を定義します。

これらのプロパティを使って、以下のようにValidationやpropsのデフォルト値を定義することができます。

function HelloMessage(props) {
  return <div>{props.text} {props.children}</div>;
};
// Validationを定義
HelloMessage.propTypes = {
  text: React.PropTypes.string.isRequired, // string型で必須
  children: React.PropTypes.string.isRequired // string型で必須
};
// propsのデフォルト値を定義
HelloMessage.defaultProps = {
 text: 'Hello',
 children: 'World'
};

ReactDOM.render(
  <HelloMessage text='Hello'>World</HelloMessage>,
  document.querySelector('.content')
);
Validationやpropsのデフォルト値を定義

PropのValidationについては、以下をご参照ください。

注意点: Stateless functional componentsにはref属性は付与できず

Stateless functional componentsは、ref属性を使ってコンポーネントのDOMを参照するためのBacking Instanceを持っていないため、ref属性をコンポーネントに設定することができません。ref属性を付与したとしてもnullが返されます。

ただ、どうしてもStateless functional componentsのDOMにアクセスしたい場合の方法について、Reactのドキュメントに以下のような記述があります。

if a user wants to find the DOM node of a stateless function component, they must wrap the component in a stateful component (eg. ES6 class component) and attach the ref to the stateful wrapper component.

つまり、通常の状態を持ったComponentでStateless functional componentsをラップして、状態を持ったComponentにref属性を設置しなくてはいけないと書いています。

どういうことかいまいちわからなかったので、以下のようなサンプルを作ってみました。

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {val: this.props.val};
  }
  handleChange() {
    this.setState({val: this.refs.myInput.value});
    console.log(this.refs.myComponent.childNodes[0].textContent);
  }
  render() {
    return (
      <div ref="myComponent">
        <HelloMessage>{this.state.val}</HelloMessage>
        <input
          type="text" value={this.state.val} ref="myInput"
          onChange={this.handleChange.bind(this)}
        />
      </div>
    );
  }
}

function HelloMessage(props) {
  return <div>{props.text} {props.children}</div>;
};

ReactDOM.render(
  <App val='world' />,
  document.querySelector('.content')
);
親コンポーネントからref属性で子のStateless functional componentsのDOMにアクセス

親コンポーネントとして通常の状態を持ったコンポーネントを作って、Stateless functional componentsを子コンポーネントとして設定し、さらに親コンポーネントにテキストフォームを設置して、そこに入力されたものを子コンポーネントに渡して表示するといったものです。

親コンポーネントに「myComponent」という値のref属性を付けて、テキストが入力されるたびに「this.refs.myComponent.childNodes[0].textContent」というプロパティで子コンポーネントの値を取得させるようにしました。一応、Stateless functional componentsのDOMにアクセスすることはできましたが、こういうことを言っているのでしょうかね?と言ったところです。

まとめ

Stateless functional componentsを一通り見てきましたが、結構使い道はあるんじゃないかなと思っています。とてもシンプルに書けるところがなんだかんだで一番の魅力です。この書き方は、Reactの開発元のFacebookでも推奨している書き方のようです。

次回以降のバージョンにおいて、不必要なチェックやメモリの割り当てを避けられるようになり、Stateless functional componentsに特化したパフォーマンスの最適化がなされるようです。書く側の負担も減らせるし、Stateless functional componentsはどんどん使っていきたいところですね。

入門 React ―コンポーネントベースのWebフロントエンド開発
  • 『入門 React ―コンポーネントベースのWebフロントエンド開発』
  • 著者: Frankie Bagnardi, Jonathan Beebe, Richard Feldman, Tom Hallett, Simon HØjberg, Karl Mikkelsen, 宮崎 空(翻訳)
  • 出版社: オライリージャパン
  • 発売日: 2015年4月3日
  • ISBN: 978-4873117195

関連記事

コメント

  1. ピンバック: recomposeを使ってコンポーネントをステートレスにする | TimeCrowd Blog

     

  • 必須

コメント