ReactでTodoアプリ

どうも、ぽっぽです。

今回は、Reactのドキュメントで紹介されているサンプルコードのTodoアプリを説明します。

GitHub – microsoft\frontend-bootcamp: Frontend Workshop from HTML\CSS\JS to TypeScript\React\Reduxを参考にしています。

ソース

Release onlyReact · hatpoppo/todoApp-typescript-create-react-app · GitHubにTodoアプリのソースを公開しています。この記事ではそのソースを見ながら読んで欲しいです。ぜひソースをダウンロードして中身を確認してください。

このソースはCreate React App · Set up a modern web app by running one command.を使っています。複数のツールを組み合わせて必要な設定なども行ってくれているので便利でした。

npm install
npm start

Todoアプリを動作させる場合、NodeJSのコマンドプロンプトを開いて、ソースをダウンロードして展開した場所(package.jsonファイルがある)に移動します。1行目のコマンドを実行します。これはpackage.jsonに記載しているパッケージをインストールします、インストールが終わるまで数十秒ほど待ちます。
2行目のコマンドを実行するとブラウザにTodoアプリが表示されます。

終わるときは、コマンドプロンプト上でCtrl + Cをタイプします。

Componentとファイルの管理

最初のステップはアプリケーションをComponentに分解することです。

  • TodoHeader
  • TodoList
  • TodoListItem
  • TodoFooter

Componentは、ファイルごとに管理できます。それぞれモジュールとして管理され、importexportを使います。
Componentを定義してexportキーワードを付与します。

export class TodoHeader extends React.Component<TodoHeaderProps, TodoHeaderState> {
...

利用するときは、importキーワードを使います。

import { TodoHeader } from "./components/TodoHeader";

TypeScript

ダウンロードしたソースはTypeScriptで記述しています。JavaScriptでは書く場合は拡張子がjsxでした。ソースではtsxになっています。

抽象化

複雑なComponentの繰り返しを表示する場合、通常は新しいComponentを作成する必要があります。

Todoアプリの場合TodoListItemです。

Index Signatures

TodoはIndex Signaturesと呼ばれるObjectで管理されています。

ひとまず、簡単にObjectについて立ち返ります。

let o = {
    'name': 'Mike',
    'blood': 'A',
    'height': '170'
};
console.log(o.name);     //"Mike"
console.log(o['name']);  //"Mike"

オブジェクトにアクセスするには上記のように、2通りあります。よく使うのは.keyでのアクセスでしょう。

Index Signaturesは、このkeyを利用します。今回のTodoアプリの場合、keyを追番で追加していくイメージです。keyをかぶらないようにTodoをObjectに追加しています。

let o = {
    '1': 'apple',
    '2': 'arange'
}

実際にTodoListのソースを見ると

const filterdTodos = Object.keys(todos).filter(id => {
      return filter === "all" || (filter === "completed" && todos[id].completed) || (filter === "active" && !todos[id].completed);
    });

Object.keysというのは、引数のObjectkeyを配列に格納します。 Object.keys(o)の場合で例えると、['1', '2']が返ってきます。

filterは配列のメソッドです。引数として与えられたテスト関数を各配列要素に対して実行し、それに合格したすべての配列要素からなる新しい配列を生成します。(参照:Array.prototype.filter() – JavaScript | MDN

テスト関数では、新しいTodoを生成しています。注目はtodos[id].completedです。すべてのkeyに対してObjectを操作しています。

Stateについて

ReactではComponentの階層を上から下の1方向だけデータの流れがあります。Stateはそれを定義したComponentでだけ変更出来ます。下のComponentから変更する場合は、イベントハンドラーを下のComponentに渡します。

具体的な箇所の一例としてTodoAppでは、TodoHeaderを呼んでいます。PropsaddTodo_addTodo_のイベントハンドラーを指定しています。filterfilterStateを指定しています。

controlled とuncontrolled

<input><checkbox>のようなform要素はcontrolleduncontrolledが存在します。Todoアプリではcontrolledで実装しています。

TodoHeaderではTodoを追加する<input>において、入力値をStateに保存して、onChangeメソッドで入力が変更されるごとにStateを更新しています。

TodoListItemでは<checkbox>でタスクのcompleteonChangeメソッドでTodoAppのメソッドをコールバック関数で更新しています。

controlleduncontrolledの違いを理解することは不要なバグなどの発生をなくすため重要です。詳しくはForms – Reactを参照してください。

静的型付け

JavaScriptは型を動的に解決します。アプリケーションが複雑になると、型が明示されていないとComponentを実装する上で不便になってきます。どんな型でPropsStateを扱うかをTypeScriptを使って明示していくことが出来ます。

TodoListを見てみると、filtertodoscompleteの3つのPropsを指定しています。
TodoListが受け取るPropsinterfaceを使って定義します。

interface TodoListProps {
  todos: Todos;
  filter: FileTypes;
  complete: (id: string) => void;
}

export class TodoList extends React.Component<TodoListProps, any> {
...

React.Componentを継承するときに、第一引数にPrps、第二引数にStateの型を指定します。

this.propsdestructuringしている箇所では、interfaseで定義していない値を記載するとエラーになります。
また、TodoAppに戻って、TodoListPropsinterfaceで定義していない値を設定すると、エラーになります。
このように、静的型付けをすることでプログラミング時にエラーを発見しやすくなります。

型の共有

アプリの型を複数のComponentで利用できるように、TodoApp.types.tsのようにすることが出来ます。必要なComponentでimportにより呼び出しています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

先頭に戻る