TodoApp の作成
完成形
https://codesandbox.io/s/k28jmw8lmr
コンポーンントの構造
このアプリケーションは、以下のようにコンポーネントを組み合わせて作成されています。
- TodoApp.js (アプリケーション全体のためのコンポーネント)
- AddTodo.js (state にtodo を追加するためのコンポーネント)
- List.js (state を元に List を作成するコンポーネント)
state の構造
- todos: todo リストの内容のための state
- nextId: 次に追加するリストの内容に付与する ID のための state
state を変更するメソッドは親コンポーネントから子コンポーネントに渡す
state を変更するための「addTodo や deleteTodo」といったメソッドは、一番親になるコンポーネントである TodoApp にもたせて、子コンポーネントに渡していく。これが React 設計の基本です。
手順1: TodoApp のコンポーネントを配置する
単純にコンポーネントを配置します。
https://codesandbox.io/s/m5jzz7nv0p
手順2: state を与え、List コンポーネントに渡す
https://codesandbox.io/s/q816304yn9
- TodoApp.js に state を持たせる
- this.state.todos を List に渡す
- List が、受け取った props を元に ul,li を返す
手順3: AddTodo コンポーネントを作り込む
https://codesandbox.io/s/xvpk08v9pw
import React from "react";
export class AddTodo extends React.Component {
constructor(props) {
super(props);
// input の内容のための state
this.state = { title: "" };
}
render() {
// form の内容を作り込む
return (
<div>
<h2>AddTodo</h2>
<form onSubmit={this.handleSubmit}>
<input value={this.state.title} onChange={this.handleChange} />
<input type="submit" value="Add to todo list" />
</form>
</div>
);
}
// input の内容を変更した場合に発動するメソッド
handleChange = event => {
// value を取得する
const title = event.target.value;
this.setState({ title: title });
};
// submit した場合に発動するメソッド
handleSubmit = event => {
// ページ遷移を止める
event.preventDefault();
alert(this.state.title);
this.setState({ title: "" });
};
}
手順4: state を変更するメソッドを作成し、子コンポーネントに渡す
https://codesandbox.io/s/8l8jvmy36l
TodoApp.js 内の state を変更するメソッド
addTodo = title => {
this.setState({
todos: [...this.state.todos, { id: this.state.nextId + 1, title: title }],
nextId: this.state.nextId + 1
});
};
// 新しい配列を作成するシンタックス
// 配列に、オブジェクトを追加し、新規配列を作成する
[...配列, {新しく追加したいオブジェクト}]
同じく TodoApp.js 内で addTodo メソッドを、AddTodo コンポーネントに渡す
render() {
// state を変更するための addTodo メソッドを
// AddTodo コンポーネントに渡す
return (
<div>
<h2>TodoApp</h2>
<AddTodo addTodo={this.addTodo} />
<List todos={this.state.todos} />
</div>
);
}
AddTodo.js 内で受け取ったメソッドを実行する
handleSubmit = event => {
event.preventDefault();
// 親コンポーネントから受け取った state を変更するメソッドを実行する
// 受け取ったものなので props の中にある
this.props.addTodo(this.state.title);
this.setState({ title: "" });
};
手順5: todo を削除するメソッドを作成し、子コンポーネントに渡す
https://codesandbox.io/s/jlwk292mw
TodoApp.js
// 削除するメソッドの追加
// filter を使って、id が一致するものを取り除く
deleteTodo = id => {
this.setState({
todos: this.state.todos.filter(todo => {
return todo.id !== id;
})
});
};
render() {
// deleteTodo を子コンポーネントに渡す
return (
<div>
<h2>TodoApp</h2>
<AddTodo addTodo={this.addTodo} />
<List deleteTodo={this.deleteTodo} todos={this.state.todos} />
</div>
);
}
List.js
// button を追加し、
// click 時に 受けとった deleteTodo を実行する
render() {
const list = this.props.todos.map(todo => {
return (
<li>
#{todo.id} {todo.title}{" "}
<button
onClick={() => {
this.props.deleteTodo(todo.id);
}}
>
delete
</button>
</li>
);
});
手順6: リファクタリング
https://codesandbox.io/s/k28jmw8lmr
key にユニークな値を与えます。 React はどの要素が変更されたのか効率的に判断するために、配列内に複数要素がある場合には key を必要とします。
List.js
const list = todos.map(todo => {
return (
<li key={todo.id}>
#{todo.id} {todo.title}
<button
onClick={() => {
deleteTodo(todo.id);
}}
>
delete
</button>
</li>
);
});
//this.state.todos のように書くと長くなるので、以下のようにすると良い。
const { deleteTodo, todos } = this.props;
const { todos, nextId } = this.state;