前回は ToDo アプリケーションのバックエンドの実装を行いました. 今回は前回作成した API を用いて ToDo アプリケーションのフロントエンドを実装していきたいと思います.
準備
HTTP クライアントとして Axios を使用します. ./font
ディレクトリに移動し, npm を用いてインストールします.
npm install axios
実装に取り掛かる前に余分なファイルを削除します.
.
├── index.html
├── node_modules [61 entries exceeds filelimit, not opening dir]
├── package-lock.json
├── package.json
├── public
│ └── vite.svg
├── src
│ ├── App.tsx
│ ├── main.tsx
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
./front/src/App.tsx
の中身を削除し, 以下のように書き換えます.
import React from "react";
const App = () => {
return <div>App</div>;
};
export default App;
ToDo 一覧の取得
まずは ToDo 一覧を取得する処理を実装します. ./front/src/App.tsx
の中身を以下のように書き換えます.
import React from "react";
import axios from "axios";
const baseURL = "http://localhost:8000/todos";
type Todo = {
id: string;
title: string;
description: string;
completed: boolean;
};
const App = () => {
const [todos, setTodos] = React.useState<Todo[]>([]);
const getTodos = async () => {
const response = await axios.get(baseURL);
setTodos(response.data);
};
React.useEffect(() => {
getTodos();
}, []);
return (
<div>
<h1>Todo List</h1>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<input type="checkbox" checked={todo.completed} onChange={() => putTodo(todo)} />
<span style={{ paddingRight: "2rem" }}>{todo.title}</span>
<span style={{ paddingRight: "2rem" }}>{todo.description}</span>
</li>
))}
</ul>
</div>
);
};
export default App;
無事に ToDo 一覧が表示されたら成功です.
ToDo の追加
フォームを用いて ToDo を追加できるようにします.
フォームの内容を State で保持し, Submit ボタンが押されたら post リクエストを送信します.
./front/src/App.tsx
の中身を以下のように書き換えます.
import React from "react";
import axios from "axios";
const baseURL = "http://localhost:8000/todos";
type Todo = {
id: string;
title: string;
description: string;
completed: boolean;
};
type PostBody = {
title: string;
description: string;
completed: boolean;
};
const App = () => {
const [todos, setTodos] = React.useState<Todo[]>([]);
const [postBody, setPostBody] = React.useState<PostBody>({
title: "",
description: "",
completed: false,
});
const getTodos = async () => {
const response = await axios.get(baseURL);
setTodos(response.data);
};
const postTodo = async () => {
await axios.post(baseURL, postBody).then((response) => {
setTodos([...todos, response.data]);
});
};
const handleChanges = (e: React.ChangeEvent<HTMLInputElement>) => {
setPostBody({ ...postBody, [e.target.name]: e.target.value });
};
React.useEffect(() => {
getTodos();
}, []);
return (
<div>
<h1>Todo List</h1>
<input name="title" value={postBody.title} onChange={handleChanges} /> <br />
<input name="description" value={postBody.description} onChange={handleChanges} /> <br />
<button onClick={postTodo}>Submit</button>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<span style={{ paddingRight: "2rem" }}>{todo.title}</span>
<span style={{ paddingRight: "2rem" }}>{todo.description}</span>
</li>
))}
</ul>
</div>
);
};
export default App;
フォームに ToDo のタイトルと説明を入力し, Submit ボタンを押すと, ToDo が追加されます.
ToDo の更新
ToDo のチェックボックスをクリックすると, その ToDo の completed を更新します. ./front/src/App.tsx
の中身を以下のように書き換えます.
import React from "react";
import axios from "axios";
const baseURL = "http://localhost:8000/todos";
type Todo = {
id: string;
title: string;
description: string;
completed: boolean;
};
type PostBody = {
title: string;
description: string;
completed: boolean;
};
const App = () => {
const [todos, setTodos] = React.useState<Todo[]>([]);
const [postBody, setPostBody] = React.useState<PostBody>({
title: "",
description: "",
completed: false,
});
const getTodos = async () => {
const response = await axios.get(baseURL);
setTodos(response.data);
};
const postTodo = async () => {
await axios.post(baseURL, postBody).then((response) => {
setTodos([...todos, response.data]);
});
};
const putTodo = async (todo: Todo) => {
await axios
.put(`${baseURL}/${todo.id}`, {
...todo,
completed: !todo.completed,
})
.then((response) => {
setTodos(todos.map((todo) => (todo.id === response.data.id ? response.data : todo)));
});
};
const handleChanges = (e: React.ChangeEvent<HTMLInputElement>) => {
setPostBody({ ...postBody, [e.target.name]: e.target.value });
};
React.useEffect(() => {
getTodos();
}, []);
return (
<div>
<h1>Todo List</h1>
<input name="title" value={postBody.title} onChange={handleChanges} /> <br />
<input name="description" value={postBody.description} onChange={handleChanges} /> <br />
<button onClick={postTodo}>Submit</button>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<input type="checkbox" checked={todo.completed} onChange={() => putTodo(todo)} />
<span style={{ paddingRight: "2rem" }}>{todo.title}</span>
<span style={{ paddingRight: "2rem" }}>{todo.description}</span>
</li>
))}
</ul>
</div>
);
};
export default App;
チェックボックスを押した後, ページをリロードしてもチェックボックスの状態が保持されていることを確認してください.
ToDo の削除
Delete ボタンが押されたら Delete リクエストを送信し, その ToDo を削除します. ./front/src/App.tsx
の中身を以下のように書き換えます.
import React from "react";
import axios from "axios";
const baseURL = "http://localhost:8000/todos";
type Todo = {
id: string;
title: string;
description: string;
completed: boolean;
};
type PostBody = {
title: string;
description: string;
completed: boolean;
};
const App = () => {
const [todos, setTodos] = React.useState<Todo[]>([]);
const [postBody, setPostBody] = React.useState<PostBody>({
title: "",
description: "",
completed: false,
});
const getTodos = async () => {
const response = await axios.get(baseURL);
setTodos(response.data);
};
const postTodo = async () => {
await axios.post(baseURL, postBody).then((response) => {
setTodos([...todos, response.data]);
});
};
const putTodo = async (todo: Todo) => {
await axios
.put(`${baseURL}/${todo.id}`, {
...todo,
completed: !todo.completed,
})
.then((response) => {
setTodos(todos.map((todo) => (todo.id === response.data.id ? response.data : todo)));
});
};
const deleteTodo = async (todo: Todo) => {
await axios.delete(`${baseURL}/${todo.id}`).then((response) => {
setTodos(todos.filter((todo) => todo.id !== response.data.id));
});
};
const handleChanges = (e: React.ChangeEvent<HTMLInputElement>) => {
setPostBody({ ...postBody, [e.target.name]: e.target.value });
};
React.useEffect(() => {
getTodos();
}, []);
return (
<div>
<h1>Todo List</h1>
<input name="title" value={postBody.title} onChange={handleChanges} /> <br />
<input name="description" value={postBody.description} onChange={handleChanges} /> <br />
<button onClick={postTodo}>Submit</button>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<input type="checkbox" checked={todo.completed} onChange={() => putTodo(todo)} />
<span style={{ paddingRight: "2rem" }}>{todo.title}</span>
<span style={{ paddingRight: "2rem" }}>{todo.description}</span>
<button onClick={() => deleteTodo(todo)}>delete</button>
</li>
))}
</ul>
</div>
);
};
export default App;
Delete ボタンを押した後, ページをリロードしても削除された ToDo が表示されないことを確認してください.
終わりに
フロントエンドの実装は以上です. こちらは駆け足となってしまいましたが, 無事 FARM 構成のフルスタックアプリケーションを作成することができました.