Reactで作るTODOアプリ前編 - React入門

Reactで作るTODOアプリ前編 – React入門

React入門」ではこれまでReactの使い方について解説を行なってきました。それではひとまずの総集編としてRectでTODOアプリを作成していきます。今回は前編と後編に分けて前篇ではReactの機能を利用したTODOアプリを作成し、後編でコンポーネント分割を行なっていきます。

環境構築

まずは「Create React App」を利用して環境を構築していきます。

今回は「todo-app」というアプリ名にしましょう。

以下のコマンドでインストールを行い、

create-react-app todo-app

以下のコマンドでアプリを起動しましょう。

cd todo-app
npm start

雛形の作成

今回はApp.jsのみを編集していきます。まずは簡単に「JSX」で雛形を作っていきましょう。

import React, { Component } from 'react';
 
class App extends Component {
  render() {
    return (
      <div>
        <h1>TODOアプリ</h1>
        <ul>
          <li><input type="button" value="☓"> JavaScript覚える</li>
          <li><input type="button" value="☓"> jQuery覚える</li>
          <li><input type="button" value="☓"> ES2015覚える</li>
          <li><input type="button" value="☓"> React覚える</li>
        </ul>
        <input type="text"> <input type="button" value="追加">
      </div>
    );
  }
}
 
export default App;

http://localhost:3000/をブラウザで確認するとTODOリストの雛形が表示されます。

ご想像の通り追加ボタンが押されたタイミングで入力欄の内容がTODOリストに追加されていき、「☓」ボタンが押されたタイミングで削除されているのが今回作成するTODOリストの機能です。すごくシンプルですね(笑

TODOをStateで管理

次にTODOを「State」で管理する形に編集していきます。

import React, { Component } from 'react';

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      todo: [
       { title: 'JavaScript覚える' } ,
       { title: 'jQuery覚える' } ,
       { title: 'ES2015覚える' } ,
       { title: 'React覚える' }
      ]
    };
  }
  render() {
    return (
      <div>
        <h1>TODOアプリ</h1>
        <ul>{this.state.todo.map( (todo, i) => {
          return <li><input type="button" value="☓" /> {todo.title}</li>
        })}</ul>
        <input type="text" /> <input type="button" value="追加" />
      </div>
    );
  }
}

export default App;

constructorメソッドを追加しsuper()でpropsの継承を行い、そのあとにthis.stateとしてStateを定義します。constructorやsuper、ES2015で追加されたClassの継承機能ですが、Reactを利用するだけならひとまずはお約束と思って大丈夫です。

Stateではtodoという名前でTODOの内容を配列で定義しています。

JSX内でli要素では{…}内ではJavaScriptを記述できる特徴を利用してStateのtodoをループして描画しています。
このJavaScript内でもJSXが記述可能ですのでli要素にTODOの内容がはいるように設定をしております。

見慣れない属性としてli要素にkey属性が追加されておりtodoの配列のインデックス番号が付与されています。Reactではループを利用する際にkeyに対してユニークな値を指定する必要があるので気を付けてください。

追加機能作成

次に追加機能作成を作成しています。これはイベント設定を利用します。

import React, { Component } from 'react';
  
class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      todo: [
       { title: 'JavaScript覚える' } ,
       { title: 'jQuery覚える' } ,
       { title: 'ES2015覚える' } ,
       { title: 'React覚える' }
      ]
    };
    this.addTodo = this.addTodo.bind(this);
  }
  // 新規追加
  addTodo() {
    // 追加
    this.state.todo.push({
      title: this.refs.newText.value
    });
    // 保存
    this.setState({
      todo : this.state.todo
    });
    // 初期化
    this.refs.newText.value='';
  }
  render() {
    return (
      <div>
        <h1>TODOアプリ</h1>
        <ul>{this.state.todo.map( (todo, i) => {
          return <li><input type="button" value="☓" /> {todo.title}</li>
        })}</ul>
        <input type="text" />
        <input type="button" value="追加" onClick={this.addTodo} />
      </div>
    );
  }
}
  
export default App;

まず、<input type="text" ref="newText"/>というように入力欄にref属性で「newText」という名前を付けています。これは後ほど登録時にこの値を参照するためです

<input type="button" value="追加" onClick={this.addTodo}/>では追加ボタンにclickイベントを設定して、クリック時にaddTodoメソッドが実行される用に設定しています。

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

addTodo()内では、まず、this.state.todo にpushで新しいTODOを追加して、setStateで追加された項目を含むTODOを保存しています。

最後にthis.refs.newText.value='';として入力欄を空にすれば追加機能は完成です。

削除機能作成

次は削除機能を作成しましょう。

import React, { Component } from 'react';

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      todo: [
       { title: 'JavaScript覚える' } ,
       { title: 'jQuery覚える' } ,
       { title: 'ES2015覚える' } ,
       { title: 'React覚える' }
      ]
    };
    this.addTodo = this.addTodo.bind(this);
  }
  // 新規追加
  addTodo() {
    // 追加
    this.state.todo.push({
      title: this.refs.newText.value
    });
    // 保存
    this.setState({
      todo : this.state.todo
    });
    // 初期化
    this.refs.newText.value='';
  }
 
  // 削除機能
  deleteTodo(i) {
    // 削除
    this.state.todo.splice(i, 1);
    // 保存
    this.setState({
      todo : this.state.todo
    });
  }
 
  render() {
    return (
      <div>
        <h1>TODOアプリ</h1>
        <ul>
          {this.state.todo.map( (todo, i) => {
            return <li key={i}> <input type="button" value="☓"
                       onClick={() => this.deleteTodo(i)}/> {todo.title}</li>
          })}
        </ul>
        <input type="text" ref="newText"/>
        <input type="button" value="追加" onClick={this.addTodo}/>
      </div>
    );
  }
}

export default App;

まずは、li要素の削除ボタンにonClick={() => this.deleteTodo(i)}としてクリックされた際にdeleteTodo()が実行されるようにイベントを設定しています。イベントは無名関数で定義して実行時に配列のインデックス番号を引数で渡せれるようにしています。

deleteTodo(i) ではspliceを利用してthis.state.todoから削除する配列を取り除き、this.setState()で削除されたTIDOリストの設定を行なっております。

動作サンプル (1部CodePenで動くようにコードを書き換えてコメントアウトで補足を入れています)

Reactとの連携では1つ1つはそれほど難しくなく、組み合わせて様々な機能を実装しています。

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