LoginSignup
0
0

More than 1 year has passed since last update.

Web3.0検証(15)-MeteorでTODO管理アプリの開発(タスクの更新/削除)

Last updated at Posted at 2022-05-21
[前回] Web3.0検証(14)-MeteorでTODO管理アプリの開発(新しいタスクの追加機能)

はじめに

今回は、TODO管理アプリにタスク更新/削除の機能を追加します。

理論武装: Reactでフォーム要素の制御

フォーム要素の状態保持の違い

  • HTMLの場合
    • 状態保持は、フォーム要素自身で行う
      • フォーム要素は、<input><textarea><select>など
    • 状態更新は、ユーザ入力に基づく
  • Reactの場合
    • 状態保持は、コンポーネントのstateプロパティにて
    • 状態更新は、setState()関数でのみ

Reactでフォーム要素のハンドリング方法2点

  • 制御コンポーネント(controlled component)
    • Reactコンポーネントが、フォーム要素のユーザ入力をハンドリング
      • stateの更新に対し、イベントハンドラを書く
      • Reactのstate信頼できる唯一情報源(single source of truth)とみなす
    • メリット
      • state値を、他のUI要素に渡したり、他のイベントハンドラからリセット可能に
  • 非制御コンポーネント(uncontrolled component)
    • DOMが、フォーム要素のユーザ入力をハンドリング
      • refを使用し、DOMからフォーム要素値を取得
      • DOM(Document Object Model)とは
        • マークアップされたリソース(Document)をリソース要素(Object)の木構造(Model)で表現し操作可能にする仕組み
        • HTMLやXMLなどマークアップされたドキュメントを、オブジェクトの木構造モデルで表現することで、プログラムから操作・利用可能にする
        • Documentの種類、操作に用いるプログラミング言語の種類に依存しない仕様

フォーム要素ハンドリングのサンプルコード

  • 制御コンポーネントでName値を受け取る例
    • フォーム要素にvalue属性を設定し、常にthis.state.valueを表示
    • handleChangeはユーザ入力により実行され、Reactのstateを更新
class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('The name is: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}
  • 非制御コンポーネントでName値を受け取る例
    • フォーム要素にref属性を設定
    • refの値this.inputを使用し、フォーム要素値を取得
class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.input = React.createRef();
  }

  handleSubmit(event) {
    alert('The name is: ' + this.input.current.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref={this.input} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

ここから、開発続行

端末1から、Meteorアプリを実行

$ cd simple-todos-react
$ meteor run
[[[[[ ~/simple-todos-react ]]]]]

=> Started proxy.
=> Started HMR server.
=> Started MongoDB.
=> Started your app.

=> App running at: http://localhost:3000/

端末2から、フォームにチェックボックスを追加

  • Taskコンポーネントにcheckbox要素を追加
    • checkboxの状態更新にonChangeイベントを使用しないため、readOnly属性指定
    • checked属性をboolean値に指定し、非制御コンポーネントから制御コンポーネントに切り替える
    • コールバックonCheckboxClickを受け取る
      • チェックボックスがクリックされたときに呼び出される関数
imports/ui/Task.jsx
import React from 'react';

export const Task = ({ task, onCheckboxClick }) => {
  return (
    <li>
      <input
        type="checkbox"
        checked={!!task.isChecked}
        onClick={() => onCheckboxClick(task)}
        readOnly
      />
      <span>{task.text}</span>
    </li>
  );
};
  • チェックボックスの切り替え
    • isCheckedフィールドを切り替えることで、タスクのドキュメントを更新可能に
    • ドキュメントを変更する関数を作成し、Taskコンポーネントに渡す
imports/ui/App.jsx
import React from 'react';
import { useTracker } from 'meteor/react-meteor-data';
import { Task } from './Task';
import { TasksCollection } from '/imports/api/TasksCollection';
import { TaskForm } from './TaskForm';

const toggleChecked = ({ _id, isChecked }) => {
          TasksCollection.update(_id, {
                      $set: {
                                    isChecked: !isChecked
                                  }
                    })
};

export const App = () => {
          const tasks = useTracker(() => TasksCollection.find({}, { sort: { createdAt: -1 } }).fetch());

          return (
                      <div>
                        <h1>Welcome to Meteor!</h1>

                        <TaskForm/>
                        <ul>
                          { tasks.map(task => <Task key={ task._id } task={ task } onCheckboxClick={toggleChecked} />) }
                        </ul>
                      </div>
                    );
};

ブラウザでアプリを確認

ブラウザから、http://localhost:3000/にアクセス。
タスク毎に、チェックボックスが追加されています。

image.png

タスクの削除機能

  • まず、Taskコンポーネントで、削除ボタンを追加し、コールバック関数を受け取る
imports/ui/Task.jsx
import React from 'react';

export const Task = ({ task, onCheckboxClick, onDeleteClick }) => {
          return (
                      <li>
                        <input
                          type="checkbox"
                          checked={!!task.isChecked}
                          onClick={() => onCheckboxClick(task)}
                          readOnly
                        />
                        <span>{task.text}</span>
                        <button onClick={ () => onDeleteClick(task) }>&times;</button>
                      </li>
                    );
};
  • つぎ、アプリに削除ロジックを追加
    • タスク削除用関数を作成
    • TaskコンポーネントのコールバックプロパティonDeleteClickに、この関数を指定
imports/ui/App.jsx
const deleteTask = ({ _id }) => TasksCollection.remove(_id);

export const App = () => {
  ..
  <ul>
    { tasks.map(task => <Task
      key={ task._id }
      task={ task }
      onCheckboxClick={toggleChecked}
      onDeleteClick={deleteTask}
    />) }
  </ul>
  ..
}

再度、ブラウザからアプリを確認

タスク別、末尾に削除ボタンが追加されました。

image.png

試しに、先頭My new taskxボタンをクリックしてみます。
すると、タスク消えました。やったー。

おわりに

タスクの更新/削除機能を追加しました。
次回もアプリ開発続きます。お楽しみに。

[次回] Web3.0検証(16)-MeteorでTODO管理アプリの開発(タスク絞込み機能とUI改善)
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0