Next.jsでToDoアプリを作ろう!

こんにちは。デザイン・システム室の妻野です。
私は昨年10月に未経験で入社し、弊社サービス「旬をすぐに」のシステム開発に携わっています。
研修で簡単なToDoアプリを作成したので、その内容を紹介できればと思います。 使用技術はNext.js(フロントエンド言語)となります。

Next.jsとは?

Next.jsとはVercel社が開発したReactを基礎としたフレームワークです。              Webアプリケーションや静的Webサイトの開発を簡単かつ効率的に行なうことを目的としています。

ReactはUI(ユーザーインターフェイス)に長けているのに対し、Next.jsはサーバーサイド機能に優れ、ページ遷移の多い大規模なWebサイトや、高速で快適なWebアプリを開発する場合に適しています。Reactの解説は下記投稿をご覧ください。

1.完成形

まず初めに完成形をお見せします。 「Add Card」を押すとカードが追加され、「Complete」を押すと内容が消える簡単なアプリです。

2.プロジェクトの始め方

お好きなディレクトリにフォルダを作成し、ターミナルで下記コマンドを実行します。

npx create-next-app@latest 

プロジェクトの名前やApp Routerを使用するかなど聞かれますので下の画像に沿って選択してください。全て入力すると、自動的に複数のファイルが作成されます。

create-next-app@14.1.2
Ok to proceed? (y) 
✔ What is your project named? … todo-app #お好きなプロジェクト名を入れてください。
✔ Would you like to use TypeScript? … No / Yes #Yesを選択。
✔ Would you like to use ESLint? … No / Yes #Yesを選択。
✔ Would you like to use Tailwind CSS? … No / Yes #Yesを選択。
✔ Would you like to use `src/` directory? … No / Yes #Noを選択。
✔ Would you like to use App Router? (recommended) … No / Yes #Yesを選択。
✔ Would you like to customize the default import alias (@/*)? … No / Yes #Yesを選択

3.アプリケーション起動方法

次は下記コマンドを実行します。

cd todo-app #作成したプロジェクト名(ここではtodo-app)

その後、下記コマンドを実行するとNext.jsのアプリケーションが起動します。

npm run dev

ブラウザにて「http://localhost:3000/」を入力してみましょう。そして下記画像が出れば成功です。

4.事前準備

実際にファイルを作成する前にスタイルの適用を抑制します。appディレクトリ配下の「globals.css」というファイルを下記のように修正してください。

@tailwind base;
@tailwind components;
@tailwind utilities;

5.コンポーネント開発

Next.jsはコンポーネント指向の言語です。                            コンポーネントとは、再利用可能な部品でありウェブページを構成する要素のことです。                           例えば、ヘッダー、フッター、メインコンテンツなどがコンポーネントになります。         コンポーネントを使うことで、ウェブページを構成する要素を再利用できるようになります。

本アプリでも機能別にコンポーネント分けしたいと思います                   appディレクトリ配下に「components」というフォルダを作成します。               その中に「Card.tsx」というファイルと「List.tsx」というファイルを作成してください。       作成したら下記のように記述してください。

import { useState } from 'react';

interface CardProps {
  card: {
    id: number;
    title: string;
    description: string;
  };
  onDelete: () => void;
}

const Card: React.FC<CardProps> = ({ card, onDelete }) => {
  const [title, setTitle] = useState(card.title);
  const [description, setDescription] = useState(card.description);

  // カードのタイトルを更新する関数
  const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setTitle(e.target.value);
  };

  // カードの説明を更新する関数
  const handleDescriptionChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setDescription(e.target.value);
  };

  return (
    <div className="bg-white rounded shadow p-3 mb-2">
      <button className="float-right text-red-600" onClick={onDelete}>Complete</button>
      <input type="text" value={title} onChange={handleTitleChange} className="w-full mb-2 border-b focus:outline-none focus:border-blue-500" />
      <textarea value={description} onChange={handleDescriptionChange} className="w-full border rounded focus:outline-none focus:border-blue-500" />
    </div>
  );
};

export default Card;
'use client';
import { useState } from 'react';
import Card from './Card';

interface ListProps {
  title: string;
}

interface CardData {
  id: number;
  title: string;
  description: string;
}

const List: React.FC<ListProps> = ({ title }) => {
  const [cards, setCards] = useState<CardData[]>([]);

  // カードを追加する関数
  const addCard = () => {
    const newCard: CardData = {
      id: cards.length + 1,
      title: `Task ${cards.length + 1}`,
      description: ''
    };
    setCards([...cards, newCard]);
  };

  // カードを削除する関数
  const deleteCard = (id: number) => {
    setCards(cards.filter(card => card.id !== id));
  };

  return (
    <div className="p-4 border rounded shadow">
      <h2 className="text-lg font-bold mb-4">{title}</h2>
      <button className="mb-2 px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600" onClick={addCard}>Add Card</button>
      {cards.map(card => (
        <Card key={card.id} card={card} onDelete={() => deleteCard(card.id)} />
      ))}
    </div>
  );
};

export default List;

6.画面への表示

最後にappディレクトリ配下の「page.tsx」ファイルの中身を全て消します。                それから下記のように記述してください。

import List from "@/components/List";

const Home: React.FC = () => {
  return(
  <div>
    <List title="To Do" />
    <List title="In Progress" />
    <List title="Done" />
  </div>
  );
};

export default Home;

ここまで記述したら変更内容を保存してください。冒頭のアプリが完成しているはずです。

7.ポイント

ポイントは「List.tsx」の前半部分です。

'use client';
import { useState } from 'react';
import Card from './Card';

interface ListProps {
  title: string;
}

interface CardData {
  id: number;
  title: string;
  description: string;
}

~省略~

まずは、「’use client’」という表記です。このコンポーネントはStateを使っているため(クライアント側の情報)この表記をしないとエラーが発生します。

次に、「import~」という文があるかと思います。こちらは主にReactの機能や別ファイルのCard.tsxの内容をimportし、使用可能な状態にしています。

最後に「interface」ですが、簡単にいうと「型定義」です。本アプリはTypeScriptを使用しているため、この定義が必要です。(型定義をしないとエラーになります)TypeScriptを使用しているとエラーなどが発見しやすい観点からプロジェクト開発にて重宝されています。

説明を省略した部分が多々ありました。以下参考になる記事ですのでぜひご覧ください。       

Next.jsはVercel社が開発したものでデプロイ(実際のWebサーバー上に配置して、利用できる状態)が容易のため人気があるフレームワークです。ぜひポートフォリオ等、個人開発し、公開してみるのはいかがでしょうか?

さいごに

現在デザイン・システム室では、新しいメンバーを募集しています。                少しでも興味を持たれた方は、ぜひご応募ください。
皆様からのご応募、心よりお待ちしております。