画像のようなサンプルアプリを実装してみます。
公式のチュートリアル でも同じように学習できますが、この記事は
- より単純で分量が少ない
 - 日本語で書かれている
 - 同じアプリを Angular でも実装している
 - Redux 導入の前段階にできる(Redux の記事は後日作成予定)
 
これらの点が異なっています。1
サンプル実装の手順
まずは Hello world
Create React App を使い、まずは環境をセットアップします。
Create React App を npm (Yarn や npx など任意のツールで構いません)でインストールし、新規プロジェクトを作成、開発サーバーを起動します:
yarn global add create-react-app
create-react-app qiita
cd qiita
yarn start
App.js の中身を以下のように、必要最低限に書き換えます:
import React, { Component } from 'react';
class App extends Component {
  render() {
    return (
      <h1>Hello world</h1>
    );
  }
}
export default App;
ブラウザーがリロードされ、画面に Hello world が表示されたら成功です。
CodePen で環境設定をスキップする
公式チュートリアルの CodePen を流用し、ブラウザー上で試すこともできます。
その際は、以下のような違いに気をつけます:
- import/export 文なしにする
 - Component クラスは 
React.Componentとして参照する - 
ReactDOM.render()のおまじないを書く - ファイル分けはできないので、一か所に全クラスを書く
 
コードはこのようになります:
class App extends React.Component {
  render() {
    return (
      <h1>Hello world</h1>
    );
  }
}
ReactDOM.render(
  <App />,
  document.getElementById('root')
);
フォームの見た目だけ作成する
<h1>Hello world</h1> の部分をインプット要素に置き換えます:
class App extends Component {
  render() {
    return (
      <div>
        <input type="text" />
        <button>SEND</button>
      </div>
    );
  }
}
テキストボックスとボタンが表示されるのを確認したら、これをコンポーネント化し、別のファイルに切り出します2:
import React, { Component } from 'react';
import { FormApp } from './FormApp';
class App extends Component {
  render() {
    return (
      <FormApp />
    );
  }
}
export default App;
import React, { Component } from 'react';
export class FormApp extends Component {
  render() {
    return (
      <div>
        <input type="text" />
        <button>SEND</button>
      </div>
    );
  }
}
コンポーネントは、Component クラスを拡張したクラスです。
<FormApp /> のように呼び出すと、render() メソッドが実行され、その戻り値が描画されます。
ブラウザーがリロードされ、同じ見た目となれば成功です。
フォームにイベント検知機能を追加する
まずは、ボタンを押されたことを検知し、コンソールに文字を出力してみます:
export class FormApp extends Component {
  send() {
    console.log('send called!');
  }
  render() {
    return (
      <div>
        <input type="text" />
        <button onClick={this.send.bind(this)}>SEND</button>
      </div>
    );
  }
}
send() メソッドを button 要素の onClick に割り当てています。
{} の中身は JavaScript の式として扱われるので、Angular のように onClick={this.send()} としてしまうと、onClick には send() の戻り値 undefined が割り当てられます。
それでは意味がないので、そうしないよう気をつけます。
また onClick={this.send} としてしまうと、今は問題ありませんが、今後 send() 内で this を使ったときにエラーになる(this が undefined となる)ため、bind() メソッドで this を固定しています。
同様に、input 要素にも onChange ハンドラーを割り当てます:
export class FormApp extends Component {
  handleInput() {
    console.log('handleInput called!');
  }
  send() {
    console.log('send called!');
  }
  render() {
    return (
      <div>
        <input type="text" onChange={this.handleInput.bind(this)} />
        <button onClick={this.send.bind(this)}>SEND</button>
      </div>
    );
  }
}
状態 (state) を持たせ、状態を更新する
constructor を追加し、その中で特殊なプロパティ state を設定します:
export class FormApp extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: '',
      message: ''
    };
  }
constructor の引数 props と super(props) の呼び出しは、今はおまじないだと思ってください。
重要なのは this.state = {...} のほうで、ここで value と message という状態を初期化しています。
value はテキストボックスの中身、message はボタンを押すと表示されるメッセージを表すために使います:
  render() {
    return (
      <div>
        <input type="text" value={this.state.value} onChange={this.handleInput.bind(this)} />
        <button onClick={this.send.bind(this)}>SEND</button>
        <div>{this.state.message}</div>
      </div>
    );
  }
input 要素には value={this.state.value} を追加し、message は <div>{this.state.message}</div> という形でボタンの次に追加しました。
初期値を変えると、その値が画面に表示されることが確認できます。
この段階では、テキストボックスに入力することができませんが、それは正常な動きです。
次のように handleInput() を修正することで、テキストボックスへの入力が有効になります:
  handleInput({ target: { value } }) {
    this.setState({
      value
    });
  }
ES6 の文法は見慣れないかもしれませんが、以下のように書いたのと同じです:
  handleInput(event) {
    let value = event.target.value;
    this.setState({
      value: value
    });
  }
React には厳格なルールがあり、その一つがこれです。
setState() メソッド以外に state を変える方法がないという点です。
たとえユーザーの入力であっても、input 要素の value が value={this.state.value} となっている限り、テキストボックスの中身は this.state.value のままで不変です。
Angular は value の変化を自動で this.state.value に反映し、state の中身を変えてくれますが (two-way binding)、React は onChange イベントを呼び出すだけです。
わざわざ setState() を呼び出すのは無駄に見えますが、明示的に書かせることで、コードを読んでデータの流れがすぐわかるという利点を得られるのです。
この FormApp 程度ではその恩恵はありませんが、そういうものだと思って進んでください。
テキストボックスに入力が可能で、ボタンを押すとコンソールにログが出る状態のアプリができていれば、成功です。
ボタンの機能を追加する
最後の機能を追加して、アプリを完成させます。
send() を以下のように修正します:
  send() {
    const { value } = this.state;
    this.setState({
      value: '',
      message: value
    });
  }
value、すなわちテキストボックスを空にし、div の中身に値を移しています。
ボタンを押すとテキストボックスが空になり、そのすぐ下に中身が移るアプリができていれば、成功です。
Refactoring: チュートリアルどおりのコードスタイルに近づける
constructor() と render() の中身を修正します:
export class FormApp extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: '',
      message: ''
    };
    this.handleInput = this.handleInput.bind(this);
    this.send = this.send.bind(this);
  }
  // ...
  render() {
    return (
      <div>
        <input type="text" value={this.state.value} onChange={this.handleInput} />
        <button onClick={this.send}>SEND</button>
        <div>{this.state.message}</div>
      </div>
    );
  }
}
render() が呼ばれるたびに bind() メソッドを呼ぶのではなく、constructor 内で呼ぶようにして計算コストを下げました。
この規模のアプリでわざわざそうしたのは、性能を気にしたからではなく、constructor の中身に定型句を揃えたかったからです。
super(props)、state の初期化そしてメソッドの bind という 3 つのおまじないです。
最終的なコード
import React, { Component } from 'react';
import { FormApp } from './FormApp';
class App extends Component {
  render() {
    return (
      <FormApp />
    );
  }
}
export default App;
import React, { Component } from 'react';
export class FormApp extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: '',
      message: ''
    };
    this.handleInput = this.handleInput.bind(this);
    this.send = this.send.bind(this);
  }
  handleInput({ target: { value } }) {
    this.setState({
      value
    });
  }
  send() {
    const { value } = this.state;
    this.setState({
      value: '',
      message: value
    });
  }
  render() {
    return (
      <div>
        <input type="text" value={this.state.value} onChange={this.handleInput} />
        <button onClick={this.send}>SEND</button>
        <div>{this.state.message}</div>
      </div>
    );
  }
}
以上でおしまいです。
おつかれさまでした。
Angular で実装した場合
参考として、Angular による実装を載せておきます:
import { Component } from '@angular/core';
@Component({
  selector: 'app-form-app',
  template: `
    <div>
      <input type="text" [(ngModel)]="value">
      <button (click)="send()">SEND</button>
      <div>{{message}}</div>
    </div>
  `
})
export class FormAppComponent {
  value = '';
  message = '';
  send() {
    const value = this.value;
    this.value = '';
    this.message = value;
  }
}
