Reactでtodoアプリを作ろう②:機能実装(Todoの追加)
前回は見た目を整えるところまで行ったので、
今回はTodoリストを追加するプログラムを実装していきます。
どのコンポーネントで何を管理するか
Reactのプログラムを書く上で大事なのがStateの管理場所とその内容だと思います。
今回は次のように設定します。
Appコンポーネント:全てのTodo(Addボタンで追加されていく)
Formコンポーネント:入力されたtitleとcontentの値
(AddされるとAppのstateに追加され、空になる)
まずはFormコンポーネントの作成から始めます。
①Form:入力されたTitleとContentの値をstateで管理する
このアプリではHookを使ってStateを管理していきますので
まずは以下のように宣言します。
//Form.jsx //useStateをimportする import React, {useState} from 'react'; //titleとcontent各々で宣言 const[toDoTitle, setToDoTitle] = useState(""); const[toDoContent, setToDoContent] = useState(""); (state変数名) (state変更関数) (()内は初期値)
宣言後に、値を変更したいタイミングでstate変更関数を呼び出し、
変更したい値を引数として渡すことで、stateが変更されます。
今回はinput・textareaに入力された値を取得し、stateへセットしたいので
input、textareaにonChange属性を追加し、state変更関数を呼び出します。
そのまま呼び出すことも可能ですが、
可読性やメンテナンス性を考えて関数化しています。
//Form.jsx //state変更関数をさらに関数化 const handleToDoTitleInputChange = (e) => { setToDoTitle(e.target.value); } const handleToDoContentInputChange = (e) => { setToDoContent(e.target.value); } //onChange属性の追加 <input type="text" className="form-control shadow" onChange={handleToDoTitleInputChange} value={toDoTitle} /> <textarea className="form-control shadow" onChange={handleToDoContentInputChange} value={toDoContent} ></textarea>
Reactにおけるvalueの設定について
input要素のvalue属性には、初期値を設定することがこれまで多かったのですが、
Reactでは挙動が異なるため少し注意が必要です。
※少しハマりました....
フォーム要素はReact側にコントロールされるため、値を変更するにはstateの変更も必要になります。
value属性にuseStateの値を代入することで、ユーザーが編集できるようにします。
こちらの記事に詳しい記述があります。
参考:qiita.com
ここまでで、入力した値を取得しそれぞれを、
toDoTitle・toDoContentへ代入することができました。
次は、この値をAppコンポーネントのstateへセットしていきます。
③AddボタンでAppコンポーネントのstateを変更する
必要な作業は以下になります。
●Appコンポーネント●
(1)useStateの宣言
(2)stateに値を追加する関数の作成(addToDoList関数)
(3)(2)で作成した関数をFormコンポーネントへpropsとして渡す
●Formコンポーネント●
(1)Appコンポーネントへ値を渡す関数の作成(callAddToDoList関数)
(2)AddボタンにonClickイベントを設定し、(1)で作成した関数を呼び出す
上記内容をコードに起こすと....
//App.jsx const App = () => { //useStateの宣言 const[toDoList, setToDoList] = useState([]); //FormからTitleとContentを受け取りstateへ追加する関数 //それぞれのキーを「title」、「content」に設定 const addToDoList = (Title, Content) => { setToDoList(toDoList.concat({"title": Title, "content": Content})) } return( //addToDoList関数をpropsとして渡す <Form addToDoList={addToDoList}/> );
//Form.jsx //Appコンポーネントからpropsで渡ってきたaddToDoList関数 //にFormで管理しているtoDoTitleとtoDoContentを渡す //※e.preventDefault();は再レンダリングをキャンセルしています const callAddToDoList = (e) => { e.preventDefault(); props.addToDoList(toDoTitle, toDoContent); } //onClickイベントにcallAddToDoList関数を設定 <button className="btn btn-success" type="submit" onClick={callAddToDoList}> Add </button>
toDoListに要素を追加する際に使用しているconcat関数については
こちらを参照ください。
これで、Addボタンをクリックすることで
AppのStateが更新されるようになりました。
④ToDoListコンポーネントでTodoを表示
最後に、Todoの表示です。
ユーザーが入力したtodoはAppコンポーネントのToDoListで
オブジェクト型で管理されています。
なので各要素を一つずつ表示させるために、mapでイテレートします。
このとき、Reactでは各要素それぞれに、ユニークなキーが必要になるので
第二引数に「index」を設定し、この値を、イテレートで生成される一番高い階層の要素に設定します。
参考:
reactjs.org
ToDoList内の値は、設定したキーであるtitleとcontentでアクセスできるので
表示したい場所で{item.title}、{item.content}を記述。
さらにlengthプロパティでToDoList要素数を取得して
タスクがいくつ残っているかも表示しています。
//ToDoList.jsx const ToDoList = (props) => { const toDoListItems = props.toDoList.map( (item, index) => { return( <div key={index} className="card toDo-item"> <div className="card-body"> <h5 className="card-title">Title: {item.title}</h5> <p className="card-text">Content: {item.content}</p> </div> </div> ); } ); return( <div className="toDo-list"> //タスクの数を表示 <h1>Your Tasks: {props.toDoList.length}</h1> <div> {toDoListItems} </div> </div> ); }
これで入力したTodoが表示されるようになりました!
このような感じで動くはずです。
今回は以上になります!
↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック