覚えておきたいReactの実装でよく使う基本的な構文(書き方)

Reactを勉強し始めた頃は、その概念はわかったとしても、実際にコードを書いてみようとすると、どう書いていいかわからず手が止まってしまう人もいるかと思います。jQueryをバリバリ書いていた人でも、Reactを書こうとすると最初は戸惑ってしまうっていうのはよく聞く話です。ある意味Reactの書き方は特殊です。まずは書き慣れる必要があるかと思います。今回の記事では、初心者向けとしてReactの実装でよく使う基本的な構文を紹介します。今回紹介する構文を覚えれば、ほぼほぼReactの仕組みも理解できるようになり、その後の学習も楽になるかと思います。ぜひ参考にしてみてください。

reactjs

ファイルを読み込む時の構文

ReactでUI(ユーザーインターフェース)を実装する際は、一般的にnpm経由でreactreact-domをプロジェクトディレクトリ内にインストールして、ファイルに読み込んで使うようにします。以下は、ES6(ECMAScript 6)importを使った読み込み方法となります。ファイルの読み込みは基本中の基本なのでこれは必ず覚えておく必要があります。(複雑なことをしないのであれば、scriptタグで読み込むことも可能ですが。)

import React from 'react';
import ReactDom from 'react-dom';

コンポーネントを作る時の構文

Reactは、コンポーネント指向のライブラリです。コンポーネント単位で細かくUIの部品を作り、最終的にそれらのコンポーネントを組み合わせてアプリケーションを構築します。Reactでは、そのコンポーネントを役割に応じていくつかの方法で作ることができます。まず覚えておきたいのが、以下の2つのコンポーネントとなります。

  • Functional Component
  • Class Component

Functional Component

Reactのコンポーネントは、このFunctional Component(Stateless Functional Component / SFC)のスタイルで書くのが基本です。JavaScriptの関数で書くコンポーネントです。JSXと呼ばれる構文を用いてUIを構築し、関数の戻り値として返します。内部で状態は持たず、コンポーネントで使用したい値はpropsとして引数を通して受け取ります。関数名の頭文字は大文字となります。ES6のアローファンクション構文で書くとかなりシンプルに書くことができます。受け取ったpropsに応じたViewを返すだけのものなので、テストも楽というのが特徴です。

const App = (props) => {
  return (
    <div>
      <h1>Hello World</h1>
    </div>
  );
};

// 以下のように無駄な部分を削ぎ落として超シンプルに書くことも可能
const App = props => <div><h1>Hello World</h1></div>;

// ES5で書いた場合
function App(props) {
  return (
    <div>
      <h1>Hello World</h1>
    </div>
  );
}

Class Component

Class ComponentはES6のClass構文を使って書きます。render()メソッドの戻り値にJSXと呼ばれる構文を用いてUIを構築します。このClass Componentは、内部にプライベートな状態(state)を持たせることができたり、コンポーネントの生成状況に応じて呼び出されるライフサイクルメソッドを持っていたりします。このような機能を持っているため、Class Componentは主に親コンポーネントとして使用し、Functional Componentで作った子コンポーネントに、保持している値を伝播させていくアプリケーションのハブのような役割を担わせます。

class App extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello World</h1>
      </div>
    );
  }
}

コンポーネントを書く際の注意点

コンポーネントはReact Elementと呼ばれるもので構築されます。それをJSXで書くことになりますが、その際に次のことに気をつけてください。コンポーネントを構成するReact Elementの階層は、必ず一つのルートElementから始めるようにします。最上階層に同レベルのReact Elementがあった場合はエラーとなります。

// これはOK
const App = (props) => {
  return (
    <div>
      <h1>Hello World</h1>
      <h2>Hello React</h2>
    </div>
  );
};

// これはNG
const App = (props) => {
  return (
    <div>
      <h1>Hello World</h1>
    </div>
    <h2>Hello React</h2>
  );
};

初期State(状態)を設定する時の構文

上で書いたようにClass Componentは内部に状態を持つことができます。classにconstructor()メソッドを追加して、this.stateにオブジェクトとして初期Stateの値を定義します。ES6のclass構文では、明示的にsuper()メソッドを呼び出すようにします。

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value1: 'foo',
      value2: [ 'bar', 'baz' ],
    };
  }
  render() {
  // 省略
  }
}

StateをPropsを通して受け取る時の構文

Class Componentで保持しているState(状態)の値は、それに紐づいている子コンポーネントからpropsを通して受け取ることができます。Class Componentに子コンポーネントを紐づけるには、子コンポーネントを独自タグ(独自要素)にして親コンポーネントに挿入します。挿入した子コンポーネント要素の中に「任意の名前」をつけた属性を設け、渡したい値を指定します。そうすることで、propsオブジェクトの中に値がセットされます。子コンポーネントはその「名前」を通してpropsから値を受け取ります。(ちなみに、受け取ったpropsの値は読み取り専用となり、子コンポーネント側で変更してはいけません。)

// 親コンポーネント
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value1: 'foo',
      value2: [ 'bar', 'baz' ],
    };
  }
  render() {
    return (
      <div>
        <Child1 data={this.state.value1} />
        <Child2 data={this.state.value2} />
      </div>
    );
  }
}

// Functionalコンポーネントで受け取る場合
const Child1 = (props) => (
  <div>
    {props.data}
  </div>
);

// Classコンポーネントで受け取る場合
class Child2 extends React.Component {
  render() {
    return (
      <div>
        {this.props.data}
      </div>
    );
  }
}

propsをバリデートする時の構文

propsを通して値を受け取る子コンポーネント側で、propsのバリデートを行うことができます。コンポーネントに付属しているpropTypesプロパティを使って、受け取るpropsの値を一つ一つの型のチェックや必須チェックなどを行います。なお、制約に違反していた場合でも、エラーとはならず、コンソール上に警告として表示されるだけとなります。

const Child = (props) => (
  <div>
    {props.data}
  </div>
);
Child.propTypes = {
  data: React.PropTypes.string,
};

バリデーションの書き方は以下を参照してください。

ちなみに、propsのバリデーションは、FlowTypeScriptを使って行うことも可能です。

イベントハンドラを設定する時の構文

Viewで発生したイベントに対するイベントハンドラは、Class Componentのメソッドとして設定します。設定したイベントハンドラは、JSX内にonClickのように「on○○○(キャメルケースで書く)」として設置することができます。またコールバック関数として実行する際にちゃんと関数内の「this」が機能するように、constructor()内でthisをbindしておく必要があります

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value1: 'foo',
      value2: [ 'bar', 'baz' ],
    };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    // bind(this)をしておかないと以下のthisが機能しない
    alert(this.state.value1);
  }
  render() {
    return (
      <div onClick={this.handleClick}>
        <Child data={this.state.value1} />
      </div>
    );
  }
}

state(状態)を更新する時の構文

Class Componentで保持しているstateは、setState()メソッドを呼び出すことで、更新することができます。逆に言うと、それ以外の方法でstateを更新してはいけませんsetState()メソッドが呼び出されると、Class Component内のrender()メソッドが呼び出され、全体のコンポーネントのレンダリングが行われるようになっています。

setState()

setState()メソッドは以下のように指定します。

setState(nextState, callback)

第一引数(nextState)には、更新後のstateの値を「オブジェクト」「関数」で渡すようにします。関数で渡す場合は、更新前のstatepropsを引数で受け取れるようになっていて、setStateする前にそれらの値を使用することができます。「更新後のstate」が渡されると、「更新前のstate」とマージされ、stateが新しい状態に更新されます。

// オブジェクトで渡す場合
setState({ value1: 'bar' })

// 関数で渡す場合1
setState(() => ({ value1: 'bar' }))

// 関数で渡す場合2
setState((previousState, currentProps) => {
  if (typeof previousState.value1 === 'string') {
    return { value1: 'bar' };
  }
  return { value1: 'baz' };
});

JSX内にsetStateを埋め込む

setStateはClass Componentのメソッドなので、thisをつけて呼び出すことができます。以下のようにJSX内のイベントハンドラとして指定しておくと、イベントが発生した際にsetState()メソッドの処理を走らせ、stateを更新し、render()メソッドによるレンダリングを行わせることができます。

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value1: 'foo',
      value2: [ 'bar', 'baz' ],
    };
  }
  render() {
    return (
      <div onClick={() => this.setState({ value1: 'bar' })}>
        <Child data={this.state.value1} />
      </div>
    );
  }
}

子コンポーネントで発生したイベントによりstateを更新するときの構文

上記で説明してきたことの応用編となります。処理の書き方のポイントは以下の通りです。

  • setStateを走らせるメソッドをclassコンポーネント内で定義
  • 任意の名前を付けてメソッドを子コンポーネントに渡す
  • 子コンポーネントではpropsを通してメソッドを受け取る
  • 子コンポーネント内にイベントハンドラを設定し、受け取ったメソッドをセットする
  • 受け取るメソッドのバリデートも可能

実際の構文は以下のようになります。子コンポーネント側でイベントが発生すると、受け取ったメソッドが呼び出され、setStateが走ります。それにより、親コンポーネント内のstateが更新され、render()メソッドによるレンダリングの処理が走るようになります。

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value1: 'foo',
      value2: [ 'bar', 'baz' ],
    };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState(() => {
      return { value1: 'bar' };
    });
  }
  render() {
    return (
      <div>
        <Child data={this.state.value1} handleClick={this.handleClick} />
      </div>
    );
  }
}

const Child = (props) => (
  <div onClick={props.handleClick}>
    {props.data}
  </div>
);
Child.propTypes = {
  handleClick: React.PropTypes.func,
  data: React.PropTypes.string,
};

Virtual DOMを生DOMにレンダリングする時の構文

親コンポーネントのrender()メソッドで返されたレンダリングの実態はReact特有の「Virtual DOM(仮想DOM)」と呼ばれるものとなっています。このVirtual DOMでできたReactコンポーネントのツリーを最終的にブラウザで表示できる生DOMのツリーに変換してあげる必要があります。react-domのrender()メソッドの第一引数に「親コンポーネント(JSXの形で)」、第二引数に「実際にhtmlファイルで表示させる部分の要素」を渡すようにします。(第三引数にはコールバック関数を渡すことができます。)

// HTMLファイル内の<div class="content"></div>内にレンダリング内容を表示させるとして

ReactDom.render(
  <App />,
  document.querySelector('.content')
);

まとめ

以下は、上記で説明した構文をすべて含めて書いたReactのサンプルとなります(ただ文字列の「foo」が表示されていて、その「foo」をクリックすると「bar」に代わるだけのものです)。コピペしていろんな部分を自由に変更して使ってみてください。

// ファイルのインポート
import React from 'react';
import ReactDom from 'react-dom';

/* ========================== */

// 親コンポーネント(Class Component)
class Parent extends React.Component {
  // 初期stateの定義
  constructor(props) {
    super(props);
    this.state = {
      value1: 'foo',
      value2: ['bar', 'baz'],
    };
    // イベントハンドラー関数にthisをバインド
    this.handleClick = this.handleClick.bind(this);
  }
  // イベントハンドラー関数の定義
  handleClick() {
    // stateを更新
    this.setState(() => {
      return {value1: 'bar'};
    });
  }
  // レンダリング内容
  render() {
    return (
      <div>
        {// 子コンポーネントに値を渡す}
        <Child data={this.state.value1} handleClick={this.handleClick} />
      </div>
    );
  }
}

/* ========================== */

// 子コンポーネント(Functional Component)
const Child = (props) => (
  {// propsを通して値を取得}
  <div onClick={props.handleClick}>
    {props.data}
  </div>
);
// propsのバリデーション
Child.propTypes = {
  handleClick: React.PropTypes.func,
  data: React.PropTypes.string,
};

/* ========================== */

// 生DOMにレンダリング
ReactDom.render(
  <Parent />,
  document.querySelector('.content')
);

今回紹介した構文さえ覚えてしまえば、Reactの仕組みはほぼほぼ理解できたということになります。おそらく、その後の学習はすんなり行くのではないかと思います。Reactはシンプルに考えると、親コンポーネントが持っているデータを、子コンポーネントたちにバケツリレーで渡していき、データがすべて行き渡ったらレンダリングするといったもので、親のデータが更新されるたびにそれを繰り返すといったものです。

Reactの書き方に慣れるには、とにかく書いてみることが大事です。まずは今回紹介した構文を使って、何でもよいと思うので簡単なサンプルをたくさん書いてみてください。そのうち思うように書けるようになっているはずです。ご不明な部分があれば、こちらのコメント欄でもよいのでご遠慮なくお尋ねください。

Reactビギナーズガイド ―コンポーネントベースのフロントエンド開発入門
  • 『Reactビギナーズガイド ―コンポーネントベースのフロントエンド開発入門』
  • 著者: Stoyan Stefanov, 牧野聡(翻訳)
  • 出版社: オライリージャパン
  • 発売日: 2017年3月11日

コメント一覧

  • 必須

コメント