神戸学院大学 経営学部 林坂ゼミ

React 入門トップページ

« 戻る 次へ »

React 入門

JavaScript コメント掲示板アプリの開発

コメント編集機能の実装

次に,投稿済みのコメントを編集する機能を作成します.編集は /edit/123 のようにコメント id を指定した URL で行うことにします.このためにまず,src/componenst/CommentEditPage.js を作成します.Visual Studio Code ではファイルの先頭に rafce と入力して次の雛形を作成します.

src/componenst/CommentEditPage.jsimport React from 'react'

const CommentEditPage = () => {
  return (
    <div>CommentEditPage</div>
  )
}

export default CommentEditPage

次に編集ページのためのルート情報を追加します.

src/routers/routes.jsimport { Route, createBrowserRouter, createRoutesFromElements } from 'react-router-dom';
import CommentListPage from '../components/CommentListPage';
import CommentShowPage from '../components/CommentShowPage';
import CommentEditPage from '../components/CommentEditPage';

const routes = createBrowserRouter(
  createRoutesFromElements(
    <Route>
      <Route path="/" element={<CommentListPage />} />
      <Route path="/show/:commentId" element={<CommentShowPage />} />
      <Route path="/edit/:commentId" element={<CommentEditPage />} />
    </Route>
  )
);

export default routes;

ブラウザで http://localhost:3000/edit/9/ にアクセスできることを確認します.

react-2024-88

詳細ページから編集ページへリンクを作成するために,まず Link をインポートします.

src/components/CommentShowPage.js(抜粋)import { useParams, useNavigate, Link } from 'react-router-dom';

適当な場所に編集ページへのリンクを設置します.

src/components/CommentShowPage.js(抜粋)return (
  <div className="container">
    <h1>コメント</h1>
    <article>
      <div>ID: {data.id}</div>
      <div className="title">Title: {data.title}</div>
      <div className="body">Body: {data.body}</div>
      <div>最終更新: {data.updated_at}</div>
      <div>
        <Link to={`/edit/${data.id}/`}>
          コメント情報の編集
        </Link>
      </div>
    </article>
    <button
      onClick={handleBackClick}
    >コメント一覧へ戻る</button>
  </div>
)

CommentEditPage コンポーネントでは URL に含まれるコメント id が取得できていることを確認します.

src/componenst/CommentEditPage.jsimport React from 'react'
import { useParams } from 'react-router-dom';

const CommentEditPage = () => {
  const params = useParams();

  return (
    <div>CommentEditPage : {params.commentId}</div>
  )
}

export default CommentEditPage
react-2024-89

CommentShowPage コンポーネントと共通する部分が多いですが,まず API から取得した内容を画面に表示するようにします.

src/componenst/CommentEditPage.jsimport React from 'react'
import { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import axios from 'axios';

const CommentEditPage = () => {
  const params = useParams();
  const [data, setData] = useState(null);
  const url = `http://127.0.0.1:8000/comments/${params.commentId}/`;

  console.log(url);

  useEffect(() => {
    axios.get(url)
      .then(res => setData(res.data))
      .catch(err => console.log(err.message));
  }, []);

  if (data === null) {
    return (
      <div className="container">
        <h1>コメントの編集</h1>
        <article>
          <p></p>
        </article>
      </div>
    );
  }

  return (
    <div className="container">
      <h1>コメントの編集</h1>
      <article>
        <div>ID: {data.id}</div>
        <div className="title">Title: {data.title}</div>
        <div className="body">Body: {data.body}</div>
      </article>
    </div>
  )
}

export default CommentEditPage
react-2024-90

次に,編集のためのフォームを作成します.src/components/EditForm.js ファイルを作成してから rafce で雛形を作成します.

src/components/EditForm.jsimport React from 'react'

const EditForm = () => {
  return (
    <div>EditForm</div>
  )
}

export default EditForm

CommentEditPage コンポーネントの先頭で EditForm をインポートします.

src/componenst/CommentEditPage.js(抜粋)import EditForm from './EditForm';

return の中で EditForm コンポーネントを配置します.(同時に仮に表示していたタイトルなどは削除します.)

src/componenst/CommentEditPage.js(抜粋)return (
  <div className="container">
    <h1>コメントの編集</h1>
    <EditForm />
  </div>
)

EditForm コンポーネントにはフォームを配置します.

src/components/EditForm.jsimport React from 'react'

const EditForm = () => {
  return (
    <>
      <form>
        <div>
          <label>
            タイトル:
            <input
              type="text"
              className="textInput"
            />
          </label>
        </div>
        <div>
          <label>
            本文:
            <input
              type="text"
              className="textInput"
            />
          </label>
        </div>
        <div>
          <button>コメントの更新</button>
        </div>
      </form>
    </>
  )
}

export default EditForm

CommentEditPage から EditForm に必要なデータを渡します.

src/componenst/CommentEditPage.js(抜粋)return (
  <div className="container">
    <h1>コメントの編集</h1>
    <EditForm
      commentId={data.id}
      title={data.title}
      body={data.body}
      url={url}
    />
  </div>
)

EditForm では props でデータを受け取れていることを確認します.

src/components/EditForm.jsimport React from 'react'

const EditForm = (props) => {
  return (
    <>
      <p>commentId : {props.commentId}</p>
      <p>title : {props.title}</p>
      <p>body : {props.body}</p>
      <p>url : {props.url}</p>
      <form>
        <div>
          <label>
            タイトル:
            <input
              type="text"
              className="textInput"
            />
          </label>
        </div>
        <div>
          <label>
            本文:
            <input
              type="text"
              className="textInput"
            />
          </label>
        </div>
        <div>
          <button>コメントの更新</button>
        </div>
      </form>
    </>
  )
}

export default EditForm

テキストボックスに初期値がセットされ,編集できるようにします.まず,プログラムの先頭にインポート文を追加します.

src/components/EditForm.js(抜粋)import { useState } from 'react';

EditForm 関数の先頭で useState でタイトルと本文の状態を管理できるように定義します.また,handleTitleChangehandleBodyChange という2つの関数を定義します.

src/components/EditForm.js(抜粋)const EditForm = (props) => {
  const [title, setTitle] = useState(props.title);
  const [body, setBody] = useState(props.body);

  const handleTitleChange = (event) => {
    setTitle(event.currentTarget.value);
  };

  const handleBodyChange = (event) => {
    setBody(event.currentTarget.value);
  };

フォームのテキストボックスに value 属性と onClick 属性を追加します.

src/components/EditForm.js(抜粋)<div>
  <label>
    タイトル:
    <input
      type="text"
      className="textInput"
      value={title}
      onChange={handleTitleChange}
    />
  </label>
</div>
<div>
  <label>
    本文:
    <input
      type="text"
      className="textInput"
      value={body}
      onChange={handleBodyChange}
    />
  </label>
</div>

コメントの情報は CommentEditPage で管理しているので,CommentEditPage で handleEditFormSubmit 関数を定義し,EditForm に OnSubmit として渡します.

src/componenst/CommentEditPage.js(抜粋)const handleEditFormSubmit = () => {
  console.log("CommentEditPage : handelEditFormSubmit");
};
src/componenst/CommentEditPage.js(抜粋)<EditForm
  commentId={data.id}
  title={data.title}
  body={data.body}
  url={url}
  onSubmit={handleEditFormSubmit}
/>

EditForm コンポーネントではボタンが押されたときに formSubmit 関数が実行されるように指定します.

src/components/EditForm.js(抜粋)<form onSubmit={formSubmit}>

さらに,formSubmit 関数を定義します.この中で props.onSubmit を呼び出すことで,CommentEditPage コンポーネントの handleEditFormSubmit 関数が実行されます.

const formSubmit = (event) => {
  event.preventDefault();
  props.onSubmit();
  console.log("EditForm : formSubmit");
}

この時点で編集ボタンがクリックされたことを CommentEditPage コンポーネントで検知できるようになったことを確認します.タイトルや本文を書き換えて「コメントの更新」ボタンをクリックします.

react-2024-91

すると,ブラウザのコンソールにログが出力されたので,意図した通りに関数が呼び出されているようです.

react-2024-92

続いて,EditForm から編集されたタイトルや本文など必要な情報を CommentEditPage コンポーネントの handleEditFormSubmit 関数へ渡します.

src/components/EditForm.js(抜粋)const formSubmit = (event) => {
  event.preventDefault();
  props.onSubmit(props.url, title, body);
  console.log("EditForm : formSubmit");
}

CommentEditPage でその情報を受け取ります.

src/componenst/CommentEditPage.js(抜粋)const handleEditFormSubmit = (url, title, body) => {
  console.log("CommentEditPage : handelEditFormSubmit");
  console.log("url", url);
  console.log("title", title);
  console.log("body", body);
};

ブラウザで確認すると,入力した内容を取得できていることがわかります.

react-2024-93

ここでターミナル(またはコマンドプロンプト)から curl コマンドでコメントの ID を指定してコメント情報を更新する方法を確認しておきます.なお,コマンド最後の /12/ にはコメントの ID を指定することに注意してください.

% curl -X PUT -d "title=API" -d "body=test" http://127.0.0.1:8000/comments/12/ ⏎

実際に API へ更新処理を送信する処理を追加しますが,更新処理後にトップページに移動したいのでまずはプログラムの先頭で useNavigate をインポートします.

src/componenst/CommentEditPage.js(抜粋)import { useParams, useNavigate } from 'react-router-dom';

更新処理をコーディングします.

src/componenst/CommentEditPage.js(抜粋)const navigate = useNavigate();
const handleEditFormSubmit = (url, title, body) => {
  axios
    .put(url, {
      title: title,
      body: body,
    })
    .then(res => {
      if (res.status === 200) {
        console.log("PUT成功");
        navigate('/');
      } else {
        console.log("PUT失敗");
      }
    })
    .catch(err => {
      console.log(err.message);
      alert("投稿エラー!未入力または文字数超過です.");
    });
};

コメントの更新ができるようになったはずなのでブラウザで確認します.コメント一覧から「ブラウザから」のコメントを開き,編集ページへ移動します.

react-2024-118

編集ページが表示された状態です.まずはデータベースに格納された内容がそのまま表示されています.

react-2024-119

タイトルと本文を適当に変更して「コメントの更新」ボタンをクリックします.

react-2024-120

コメントが更新されて一覧ページに戻りました.

react-2024-121

EditForm.js の全体を示します.

src/components/EditForm.jsimport React from 'react'
import { useState } from 'react';

const EditForm = (props) => {
  const [title, setTitle] = useState(props.title);
  const [body, setBody] = useState(props.body);

  const handleTitleChange = (event) => {
    setTitle(event.currentTarget.value);
  };

  const handleBodyChange = (event) => {
    setBody(event.currentTarget.value);
  };

  const formSubmit = (event) => {
    event.preventDefault();
    props.onSubmit(props.url, title, body);
    console.log("EditForm : formSubmit");
  }

  return (
    <>
      <form onSubmit={formSubmit}>
        <div>
          <label>
            タイトル:
            <input
              type="text"
              className="textInput"
              value={title}
              onChange={handleTitleChange}
            />
          </label>
        </div>
        <div>
          <label>
            本文:
            <input
              type="text"
              className="textInput"
              value={body}
              onChange={handleBodyChange}
            />
          </label>
        </div>
        <div>
          <button>コメントの更新</button>
        </div>
      </form>
    </>
  )
}

export default EditForm

CommentEditPage.js の全体を示します.

src/componenst/CommentEditPage.jsimport React from 'react'
import { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import axios from 'axios';
import EditForm from './EditForm';

const CommentEditPage = () => {
  const params = useParams();
  const [data, setData] = useState(null);
  const url = `http://127.0.0.1:8000/comments/${params.commentId}/`;

  console.log(url);

  useEffect(() => {
    axios.get(url)
      .then(res => setData(res.data))
      .catch(err => console.log(err.message));
  }, []);

  const navigate = useNavigate();
  const handleEditFormSubmit = (url, title, body) => {
    axios
      .put(url, {
        title: title,
        body: body,
      })
      .then(res => {
        if (res.status === 200) {
          console.log("PUT成功");
          navigate('/');
        } else {
          console.log("PUT失敗");
        }
      })
      .catch(err => {
        console.log(err.message);
        alert("投稿エラー!未入力または文字数超過です.");
      });
  };

  if (data === null) {
    return (
      <div className="container">
        <h1>コメントの編集</h1>
        <article>
          <p></p>
        </article>
      </div>
    );
  }

  return (
    <div className="container">
      <h1>コメントの編集</h1>
      <EditForm
        commentId={data.id}
        title={data.title}
        body={data.body}
        url={url}
        onSubmit={handleEditFormSubmit}
      />
    </div>
  )
}

export default CommentEditPage

なお,Python Django で開発した API 側でスリープ処理を入れている場合は,画面が切り替わるまでに時間がかかります.その間,ユーザはシステムの状態を理解することができないので,処理中であることがわかるように何らかの表示がされるようにしてもよいでしょう.

目次に戻る