React入門

React入門」、前回の「Reactで作るTODOアプリ前編」ではこれまでの総復習として簡単なTODOアプリを作成しました。

後編の今回は前回作成したTODOアプリのコンポーネント分割をしていきます。

新たにListコンポーネントを作成してTODOの一覧部分として、入力部分はInputコンポーネントとして分割します。

Listコンポーネントの作成

まずは以下のようなListコンポーネントを作成します。

ListコンポーネントではStateの管理は行わないのでStateless Functionsでコンポーネント作成を行います。

<ul>...</ul> は前回作成したAppコンポーネントからコピーして持ってきたものです。

一箇所だけ変更を加えており 、`this.state.todo` は `props.todo` に変更して、Stateではなく親コンポーネント(Appコンポーネント)からTODO情報を Props として受取り表示するように変更しています。

function List(props){
  return (
    <ul>
    {props.todo.map( (todo, i) => {
      return (
        <li>
          <input type="button" value="☓" onClick={this.deleteTodo(i)}} />
          {todo.title}
        </li>
      )
    })}
    </ul>
  )
};

Appコンポーネントの <ul>...</ul> があった場所には <List todo={this.state.todo} /> として先ほど作成したListコンポーネントをPropsとして、現在のTODOのStateをListコンポーネントに引き渡す形にしています

class App extends Component {
  (中略)
  render() {
    return (
      <div>
        <h1>TODOアプリ</h1>
        <List todo={this.state.todo} />
        <input type="text" ref="newText"/>
        <input type="button" value="追加" onClick={this.addTodo}/>
      </div>
    );
  }
}

ソースコード / 動作サンプル

これで一覧の描画はできましたが削除機能は動作していません。削除が動作するようにカスタマイズしていきましょう。

Listコンポーネントで削除機能を実装

TODOのStateはAppコンポーネントで管理されており、削除ボタンはListコンポーネント内に存在します。

コンポーネント間で情報の受け渡しで解説した子コンポーネントから親コンポーネントの情報を操作する手法を用いて親コンポーネントのStateを操作します。

まずは、Appコンポーネント内のListコンポーネントの呼び出し時に <List todo={this.state.todo} deleteTodo={this.deleteTodo}/> としてdeleteTodoというpropsで削除用のメソッドを引き渡します。

また、constructor内では this.deleteTodo = this.deleteTodo.bind(this); を追加します。これはイベントの説明時に解説したthisを固定するためのテクニックです。

次にListコンポーネントの this.deleteTodo(i)props.deleteTodo(i) のように変更してpropsとして受け取ったdeleteTodoが実行されるように変更します。これで子コンポーネント(Listコンポーネント)から親コンポーネント(Appコンポーネント)の削除用のメソッドを操作してTODOの削除が可能になります。

ソースコード / 動作サンプル

Inputコンポーネントで登録機能を実装

次はInputコンポーネントを作成しましょう

Appコンポーネント内の新規追加用の addTodo() を次のように変更します。 もともとは this.refs.newText.value を参照してStateを更新するメソッドでしたが入力欄がInputコンポーネント内に変更されますので単純に受け取った値をStateに追加するメソッドに変更しています。

  // 新規追加
  addTodo(value) {
    // 追加
    this.state.todo.push({
      title: value
    });
    // 保存
    this.setState({
      todo : this.state.todo
    });
  }

renderメソッドは次のようにInputコンポーネントを描画するように変更しています。その際に先ほど変更したaddTodoメソッドをPropsとして引き渡しています。

  render() {
    return (
      <div>
        <h1>TODOアプリ</h1>
        <List todo={this.state.todo} deleteTodo={this.deleteTodo}/>
        <Input addTodo={this.addTodo} />
      </div>
    );
  }

次にInputコンポーネントを作成しましょう。

InputコンポーネントもListコンポーネントと同様にstateの管理は行わないのですが `refs` による参照を行っているためStateless Functionsの参照ができないのでClass構文で作成を行います。

class Input extends Component {
  constructor(props){
    super(props);
    this.addTodo = this.addTodo.bind(this);
  }
  addTodo(){
    this.props.addTodo(this.refs.newText.value);
    this.refs.newText.value='';
  }
  render() {
    return (
      <div>
          <input type="text" ref="newText"/>
          <input type="button" value="追加" onClick={this.addTodo}/>
      </div>
    )
  }
};

ここでは追加ボタンがクリックされた際にPropsで受け取っている `addTodo()` を実行して値の更新を行うようにしています。

ソースコード / 動作サンプル

これで今回の作業は終了です。

今回は練習用にコンポーネントを細かく分けましたが無理にわける必要はありません。

管理のしやすさと追加まわしやすさを考慮してどのような粒度で別けるかを検討するとよいでしょう。

次回はAjaxを利用した状態管理について解説を行います。