Next.js×TypeScriptでTODOアプリを作成する
はじめに
Next.js×TypeScriptでTODOリストを作成したので、コードを解説していきます。
コード
"use client";
import Form from "../app/components/Form";
import Title from "../app/components/Title";
export default function Home() {
return (
<main className="min-h-screen bg-gray-100 py-10 px-4 sm:px-6 lg:px-8">
<div className="max-w-md mx-auto bg-white rounded-lg shadow-md overflow-hidden">
<Title/>
<Form/>
</div>
</main>
)
}
"use client";
import React, {} from 'react';
export default function Title() {
return (
<div className="px-4 py-5 sm:px-6 bg-indigo-600">
<h1 className="text-lg font-semibold text-white">TODOアプリ</h1>
</div>
)
}
import React, { useState } from 'react';
interface Todo {
task: string;
isCompleted: boolean;
}
interface CompleteListProps {
textList: Todo[];
onDelete: (index: number) => void;
}
export default function CompleteList({ textList, onDelete }: CompleteListProps) {
return (
<ul className="mt-6 space-y-4">
{textList.map((todo: Todo, index: number) => (
<li key={index} className="flex items-center justify-between bg-white border border-gray-300 px-4 py-2 rounded-md">
<span>{todo.task}</span>
<button className="text-red-600 hover:text-red-800" onClick={() => onDelete(index)}>削除</button>
</li>
))}
</ul>
);
}
import React, { useState } from 'react';
import CompleteList from "../components/CompleteLists";
interface Todo {
task: string;
isCompleted: boolean;
}
export default function Form() {
const [todoText, setTodoText] = useState<string>('');
const [textList, setTextList] = useState<Todo[]>([]);
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setTodoText(e.target.value);
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const newTodo: Todo = {
task: todoText,
isCompleted: false
};
setTextList(prevTextList => [...prevTextList, newTodo]);
setTodoText('');
};
const handleDelete = (index: number) => {
setTextList(prevTextList => {
const updatedList = [...prevTextList];
updatedList.splice(index, 1);
return updatedList;
});
};
return (
<div className="px-4 py-4 sm:p-6">
<div className="mb-4">
<form onSubmit={handleSubmit}>
<input
type="text"
className="w-full border border-gray-300 px-3 py-2 rounded-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
placeholder="タスクを入力してください"
value={todoText}
onChange={handleInputChange}
/>
<button className="bg-indigo-600 text-white px-4 py-2 rounded-md" type="submit">追加する</button>
</form>
</div>
<CompleteList textList={textList} onDelete={handleDelete}/>
</div>
);
}
解説
コードの解説をしていきます
○コンポーネント化について
page.tsxでは、フォームの部分と、タイトルで今回は分けています。
<Title/>
<Form/>
Formコンポーネント内では、テキストを入力するフォーム部分と、登録したテキストを表示する部分でコンポーネント化しています。
//Form.tsx内に書いているコード↓
<CompleteList textList={textList} onDelete={handleDelete}/>
○propsについて
textList={textList}は、CompleteListコンポーネントで作成したテキストを渡して、onDelete={handleDelete}は削除する処理を渡しています。
interfaceで定義しているコードは、propsで値を受け取る側と、渡す側の型を定義しています。
interface CompleteListProps {
textList: Todo[];
onDelete: (index: number) => void;
}
<CompleteList textList={textList} onDelete={handleDelete}/>
受け取る側では、以下のように記述します。今回はTypeScriptを使用しているので、以下のような受け取り方になっています。
interface CompleteListProps {
textList: Todo[];
onDelete: (index: number) => void;
}
export default function CompleteList({ textList, onDelete }: CompleteListProps)
メソッドの処理内容について
次は、メソッドの処理内容についてです。
まずはフォーム部分から説明していきます。
<form onSubmit={handleSubmit}>
<input
type="text"
className="w-full border border-gray-300 px-3 py-2 rounded-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
placeholder="タスクを入力してください"
value={todoText}
onChange={handleInputChange}
/>
このコードでは、form要素とinput要素にそれぞれ作成したメソッドを割り当てています。
それぞれのメソッドについて説明します。関係する箇所を抜粋しながら説明します。
interface Todo {
task: string;
isCompleted: boolean;
}
const [textList, setTextList] = useState<Todo[]>([]);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const newTodo: Todo = {
task: todoText,
isCompleted: false
};
setTextList(prevTextList => [...prevTextList, newTodo]);
setTodoText('');
};
このコードは、登録ボタンを押した時に、フォームの内容を登録する処理に関係する処理です。
interfaceでフォームを送信する時に使用する変数に型を指定し、handleSubmit()内で、送られてきた内容を各変数に登録し。useState内で指定した、setTextList()を呼び出して、保存します。
最後のsetTodoText('')でボタンを押した後に、フォームの中身を空にする処理を行います。
interface Todo {
task: string;
isCompleted: boolean;
}
上のコードは、propsで渡す側と受け取る側のコンポーネントファイルで、受け取るデータの型を定義します。
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setTodoText(e.target.value);
};
上のコードは、input要素に入力がされるたびに、画面が再レンダリングして、setTodoTextを動かして、useStateで定義した変数(todoText)に入力内容を格納するコードです。
○表示部分
次は登録したタスクを表示する部分のコードになります。
以下がコードです。
interface Todo {
task: string;
isCompleted: boolean;
}
interface CompleteListProps {
textList: Todo[];
onDelete: (index: number) => void;
}
export default function CompleteList({ textList, onDelete }: CompleteListProps) {
return (
<ul className="mt-6 space-y-4">
{textList.map((todo: Todo, index: number) => (
<li key={index} className="flex items-center justify-between bg-white border border-gray-300 px-4 py-2 rounded-md">
<span>{todo.task}</span>
<button className="text-red-600 hover:text-red-800" onClick={() => onDelete(index)}>削除</button>
</li>
))}
</ul>
interfaceで型を定義
表示するテキストの型を定義して、
interface Todo {
task: string;
isCompleted: boolean;
}
Form.tsxから受け取ったpropsの値に対しての型付けをしています。
interface CompleteListProps {
textList: Todo[];
onDelete: (index: number) => void;
}
次は表示部分です。
<ul className="mt-6 space-y-4">
{textList.map((todo: Todo, index: number) => (
<li key={index} className="flex items-center justify-between bg-white border border-gray-300 px-4 py-2 rounded-md">
<span>{todo.task}</span>
<button className="text-red-600 hover:text-red-800" onClick={() => onDelete(index)}>削除</button>
</li>
))}
</ul>
propsで受け取った変数textListをmap関数を用いて、一覧で表示しています。
textList.map((todo: Todo, index: number) => ...) は、textList 配列の各要素に対して関数を適用し、新しい配列を生成します。各要素は todo という名前の変数に割り当てられます。また、index は現在の要素のインデックスです。
つまり、受け取ったデータの塊を「todo」に格納して、その塊ごとに「index」という変数でインデックスを割り当てて、1つずつ表示できるようにしています。
削除機能
最後に削除機能を実装しているコードです。
削除に関係しているコードを抜粋して解説していきます。
まずは以下のコードです。
メソッドを作成して、propsで渡しています。
ボタンを押したときに、その要素のindexを受け取り、そのindexに一致する要素を排除した配列をreturnする処理を書いています。
const handleDelete = (index: number) => {
setTextList(prevTextList => {
const updatedList = [...prevTextList];
updatedList.splice(index, 1);
return updatedList;
});
};
<CompleteList textList={textList} onDelete={handleDelete}/>
受け取ったメソッドを受け取り、interfaceで型を定義しています。
メソッドをボタンに割り当てて、ボタンが押されたときに実行されるように定義しています。
interface CompleteListProps {
textList: Todo[];
onDelete: (index: number) => void;
}
<button className="text-red-600 hover:text-red-800" onClick={() => onDelete(index)}>削除</button>
Discussion