一応この記事の続きです。React 入門チュートリアル①
react触ったことない方は①、stateとporpsにつまずいている方はこの記事からでも大丈夫だと思います。
今回は簡単なTODOリストアプリを作りながら、stateとporpsとカスタムコンポーネントの作成について理解してもらえればなと思います。
今回の目標
- stateとpropsの概念を理解する
- カスタムコンポーネントの作成の仕方
用意するもの
いざ、実践
リストの部分を作る
リスト部分を作りながらprops
とカスタムコンポーネントの作成
について
説明しようと思います。
カスタムコンポーネント!!ていうとすごいように聞こえますが、オリジナルのコンポーネントという意味です。
それ以上でもそれ以下でもなくて、今回の例で言うとリストを表示する(配列からリストを作成する)コンポーネントを作成するて感じです。
では早速、<script type="text/babel"></script>
内を編集していきます。
関数でカスタムコンポーネントを定義する
まずはjs(javascript)の関数で定義して、表示までやっていきます。
const TodoList = () => {
return (
<ul>
<li>ほげ</li>
<li>ほげ</li>
<li>ほげ</li>
</ul>
);
}
ReactDOM.render(
<TodoList />,
document.getElementById('content')
);
新しいところは、カスタムコンポーネントはjsの関数できるということだけです。
少し補足すると、const TodoList = () => {}
は function TodoList () {}
と同じ意味で、Reactコンポーネントは<ul></ul>
とか<li></li>
のJSXで書かれたHTMLを作成ものです。* React 入門チュートリアル①で詳しく説明してます。
これだと、表示するリストの内容が変えられないので、配列をこのTodoListコンポーネント
に渡すとリスト化してくれるように改善しましょう。
propsで外から与えられた変数にアクセスする
ここで、props
とmap()
を使って編集すると
const items = ["hoge","hogehoge","fugaga"];
const TodoList = (props) => {
return (
<ul>
{props.items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
ReactDOM.render(
<TodoList items={items} />,
document.getElementById('content')
);
items
はTodoListコンポーネント
に渡す配列を作成していて
map()
の部分を少し補足すると
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
# propsの部分を省きました
map()
がわからない方はjsの内容になるのでググってもらって、
<li key={index}>{item}</li>
のkey
はアイテムの変更の識別に必要なもので、
keyを設定しないと表示には成功しますが、コンソールに警告が出ます。
アイテムの削除や変更機能などをつけていくとエラーやバグの原因になると思うので、Reactで配列を扱うときはkeyを設定しましょう。
*keyについては他の記事で追記する予定
次にporpsの説明です。
const TodoList = (props) => {
return (
<ul>
{props.items}
</ul>
);
}
ReactDOM.render(
<TodoList items={変数} />,
document.getElementById('content')
);
*propsに関連する部分だけ抽出したもの、多分動かない
「propsで外から与えられた変数にアクセスする」でわかる人もいると思いますが
<TodoList items={変数} />
でTodoList
を呼び出す際にHTMLの属性の設定のように、変数を渡すことができて
const TodoList = (props) => {}
のようにTodoList関数に引数を設定して
TodoList関数内では{props.items}
で渡された変数にアクセスできます。
これであとはフォームを作成して、入力した内容を配列item
に追加するロジックを作れば完成です!
フォーム部分を作る
関数でカスタムコンポーネントを定義する
またもやカスタムコンポーネントの定義から、TodoList関数
はそのままにして、以下のように編集します。
class TodoApp extends React.Component {
render() {
return (
<div>
<h3>TODOアプリ</h3>
<form>
<input/>
<button>Add</button>
</form>
</div>
);
}
}
ReactDOM.render(
<TodoApp />,
document.getElementById('content')
);
ReactではES6のclass構文を使用して、コンポーネントを定義するほうがメジャーだと思います。class構文を使うことによって、class内で独自の変数state
を保持したり、メソッドを定義できるので、class構文のほうができる幅は広いです。
ではなぜ、TodoList
は関数で定義したかというと、簡単に言うとclassを作るほどのコンポーネントでないからです。もちろんclassで作っても動作します。
propsしか使わない(関数ではstate
は使用不可)ですし、将来的に関数で定義したほうが早く動作する可能性がある、シンプルにかけるので関数で定義しました。
説明するところがあるとしたら、classが呼び出されるとrenderメソッド
がreturn部分
を返してくれるので、以前と同じく<TodoApp />
という形で呼び出しができます。
#### classにstate(ステータス)を持たせる
配列items
を削除して、TodoApp
を以下のように編集します。
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = {items: ["hoge","hogehoge","fugaga"]};
}
render() {
return (
<div>
<h3>TODOアプリ</h3>
<form>
<input/>
<button>Add</button>
</form>
<TodoList items={this.state.items} />
</div>
);
}
}
これで、ブラウザを更新すると、フォームとリストが表示できるはずです。
ここでは、stateの設定と<TodoList />
で前半で定義したコンポーネントを呼び出しています。
constructor(props) {
super(props);
this.state = {items: ["hoge","hogehoge","fugaga"]};
}
上の2行は構文で、stateの内容はthis.state = {items: ["hoge","hogehoge","fugaga"]};
で設定しています。
そして設定した内容は{this.state.items}
で呼出せます。
itemsの初期値は空配列にしたいので、this.state = {items: [ ]};
に修正します。
#### フォームに入力されたものを取得する
フォームに入力に入力されたもの、取得してtextというstate(ステータス)として保持させます。
まず、新しいstateを追加します。
constructor(props) {
super(props);
this.state = {items: [], text: ''};
}
次にinputタグのonChangeイベントを使って、ステータスtext
を更新するhandleChangeメソッド
を定義します。
render() {
return (
<div>
<h3>TODOアプリ</h3>
<form>
<input onChange={this.handleChange.bind(this)} />
<button>Add</button>
</form>
<TodoList items={this.state.items} />
</div>
);
}
handleChange(e) {
this.setState({text: e.target.value});
}
}
ここからはReactというよりかはjsの実装て感じですね。
ステータスを更新するにはsetState()メソッド
を使用します。
setState({ステータス名: 更新する内容})
のように、書きます。
少しだけ補足するとbind(this)
の部分は
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {items: [], text: ''};
}
省略
<input onChange={this.handleChange} />
みたいな書き方が推奨されてます。できるだけruturn内をシンプルに保つためでしょうか。
実際にtext
が更新されているかは、適当な場所に(h3タグの下とか)に<p>{this.state.text}</p>
を追加して、実際に入力すれば確認してださい。
#### エンターを押したら、入力内容をitemsに追加する
入力内容をitemsに追加するメソッドを定義します。
this.state.text
で入力内容を取得して、setState
でitemsに追加して、更新という流れです。
handleSubmit(e) {
e.preventDefault();
const newItem = this.state.text;
this.setState((prevState) => ({
items: prevState.items.concat(newItem)
}));
}
それをformタグのonSubmitイベントに割り当てます。
<form onSubmit={this.handleSubmit.bind(this)}>
<input onChange={this.handleChange.bind(this)} />
<button>Add</button>
</form>
これで、入力したものがリスト化されるはずです!!
実際に動かしてみましょう。
いよいよ最後です!
動かしてみると、フォーム入力したものが残ったままなので、handleSubmitメソッド
が動く段階でついでにステータスtext
を空の文字列に更新しましょう。
また、inputタグのvalue属性にこれを渡してあげないと見た目上は反映されないので編集してましょう。
ついでに全体のコードを載せるとこんな感じになると思います。
<script type="text/babel">
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = {items: [], text: ''};
}
render() {
return (
<div>
<h3>TODOアプリ</h3>
<form onSubmit={this.handleSubmit.bind(this)}>
<input onChange={this.handleChange.bind(this)} value={this.state.text} />
<button>Add</button>
</form>
<TodoList items={this.state.items} />
</div>
);
}
handleChange(e) {
this.setState({text: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
const newItem = this.state.text;
this.setState((prevState) => ({
items: prevState.items.concat(newItem),
text: ''
}));
}
}
const TodoList = (props) => {
return (
<ul>
{props.items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
ReactDOM.render(
<TodoApp />,
document.getElementById('content')
);
</script>
おわり
疲れました。でも、ここまでくるとだいぶ作れるUIの幅が広がると思います。
自分はjsがちょっと怪しかったのです。何回もエラーを吐いて大変でしたが
なんとか完成してよかったです。
お疲れ様でした。
記事作成TODO
- React 入門チュートリアル③
- key(React)について
- bind(this)
- concat