React + Typescript その4

前回のあらすじ

前回、TODOリストにTODOを追加できるようになりました。

今回は 前回のソースコード
を元に、編集ボタンで登録済みのTODOのタイトルや本文を変更できるようにします。

編集ボタンの挙動を考える

コンポーネントの構造を考えると、TodoItemコンポーネントの編集ボタンのクリックリスナからTodoEditコンポーネントのStateを変更するのはムリでしょう。

Todo
 ┣ TodoEdit ←ココニ アクセス ハ デキナイ
 ┗ TodoList
   ┣ TodoItem ←ココカラ
   ┣ TodoItem
   ┣ TodoItem
   ┣ TodoItem
   ┗ TodoItem

なので、TodoItemコンポーネントからTodoコンポーネントを経由してTodoEditコンポーネントにアクセスすることを考えました。

  1. TodoItemコンポーネントの編集ボタンを押す
  2. Todoコンポーネントのクリックリスナを呼ぶ
  3. TodoEditコンポーネントのStateの値を変更する

が、
1.と2.は問題ないのですが、3.がうまくいきませんでした。

外部からStateを変更できない

TodoコンポーネントにonClick_Edit()メソッドを用意して、Propsを通じてTodoItemコンポーネントから呼ぶ。ここまではOKです。

問題はonClick_Edit()メソッドからTodoEditコンポーネントのStateの値を変更する手段がないことです。

まずそもそも、Stateはコンポーネント内部でのみ使用するものですから、当然外部からの直接操作はできません。それではTodoEditコンポーネントのPropsを介してTodoコンポーネントから値を渡し、TodoEditコンポーネント自身にStateを変更させれば、と思ったのですが、これもうまくいきません。

ではどうするのか?

Todoコンポーネントがtitlecontentを操作できるようにするため、titlecontentTodoEditコンポーネントからTodoコンポーネントに引き上げます。

編集ボタンのクリックリスナを作る

まずTodoItemコンポーネントのPropsにクリックリスナの定義を追加して、編集ボタンが押されたときにこれを呼ぶようにします。
TODOデータはTodoコンポーネントが持っているので、引数にIDさえ渡せばどのTODOの編集ボタンが押されたのかはわかります。

TodoItem.tsx

ひとつ上のTodoListコンポーネントでも同様の対応が必要です。

TodoList.tsx

最後にTodoコンポーネントにonClick_Edit()メソッドを用意してTodoListコンポーネントに渡します。これでクリックリスナの用意は完了です。編集ボタンを押すとTODOのタイトルがアラート表示されるようになっているはずです。

Todo.tsx

TodoEditコンポーネントからTodoコンポーネントへStateを移動する

前述のとおり、TodoEditコンポーネントのStateにあるtitlecontentTodoコンポーネントのStateに移動します。

Todo.tsx

まず、TodoコンポーネントのStateにtitlecontentを追加します。
編集中のTODOデータのIDも管理したいので、これも追加します。
追加したメンバ変数はコンストラクタで初期化します。

Todo.tsx

また、フォームが変更されたときに呼ぶメソッドonChange_Edit()メソッドを用意します。引数としてタイトルと本文を受け取ってsetState()メソッドでStateに反映するメソッドです。

titlecontentTodoコンポーネントで管理するようになるので、onClick_Submit()メソッドの引数としては不要になりました。onClick_Submit()メソッドの中身については後述するので、いまは空にしておきます。

setState()メソッドを呼ぶとコンポーネントの再描画が行われるので、最新のtitlecontentTodoEditコンポーネントに反映するためにTodoEditコンポーネントのPropsへこれらを渡します。
onChange_Edit()メソッドもTodoEditコンポーネントから呼んでもらわなければならないので、これもPropsへ渡します。

Todo.tsx への修正は以上です。

TodoEdit.tsx

次に TodoEdit.tsx を修正します。

まず Todo.tsx の修正で TodoEditコンポーネントのPropsにメソッドonChange_Edit()と変数titlecontentが追加されました。またonClick_Submit()メソッドの引数がなくなりました。それらをPropsに反映します。

Stateのメンバは不要になったので削除します。

TodoEdit.tsx

フォームが変更されたときに、TodoコンポーネントのonChange_Edit()メソッドを呼ぶように修正します。変更された値はeventから、そうでない値はPropsから取得します。

タイトルと本文のvalueはStateではなくPropsの値を参照します。
メソッドonClick_Submitの引数がなくなったので、呼出しも修正します。

以上でtitlecontentTodoEditコンポーネントからTodoコンポーネントへ移動できました。

登録と更新

まだonClick_Submitメソッドが空っぽなので、TODOデータの登録ができません。TODOデータが登録または更新できるようにします。

ちょっと長ったらしいですが、要はIDが一致するデータが既にあるなら更新、ないなら登録(追加)しているだけです。

Todo.tsx

編集ボタンが押されたときに、Stateの値を更新します。
setState()メソッドが呼ばれることでTodoEditコンポーネントも再描画されるので、編集ボタンを押したTODOデータがフォームに反映されます。

Todo.tsx

以上で、編集ボタンが実装できました。

新規ボタン

最初の1件は登録できますが、以降は登録ボタンを押しても最初のTODOデータが更新されるだけになっています。IDがリセットされないためです。

なので、新規ボタンを押すとIDとタイトルと本文がリセットされるようにします。

新規ボタンのクリックリスナonClick_Clear()を用意して、Stateをリセットする処理を記述します。
onClick_Clear()メソッドはTodoEditコンポーネントから呼ぶため、Propsに加えます。

Todo.tsx

TodoEditコンポーネントにも必要な変更を加えます。
PropsにonClick_Clear()メソッドを加え、新規ボタンクリックで呼び出します。

TodoEdit.tsx

新規ボタンの実装は以上です。

削除ボタンは?

削除ボタンの実装がまだ残っています。
説明がめんどうくさいここまでの知識で削除ボタンも実装できるので、試してみましょう。

おわりに

以上でReact+Typescriptの一連の記事を終わります。
ここまでの ソースコード です。

このままではリロードするとTODOデータは消えてしまいますが、APIでデータを操作できるサーバを用意して、要処でAPIを呼ぶようにすればよいので、難しいことではないでしょう。

Reactがよくわからないまま思いつきで書いてみたのでPropsやStateやメソッドの場所や内容がコロコロと変わってしまってみっともない限りでしたが、Reactの特性を理解して設計できるようになればもっとスマートに書けるようになるような気がします。