2
3

More than 1 year has passed since last update.

Next.jsで簡単なTodoページを作成してみた

Last updated at Posted at 2022-03-07

初めに

今回、久しぶりにプログラミングの勉強をしようということで流行りのReactを勉強しました。
どうせならNext.jsも使って簡単なものを作ろうと思い、今回はTodoアプリを作成しました。

環境

  • Docker
  • Next.js
  • TypeScript
  • TailwindCSS

Dockerでの環境構築

ネットでたくさん出回っていますが、私は下記サイトを参考にしました。
https://zenn.dev/tasuya/articles/da033574b85e6d

ヘッダーを追加する

ほとんどのサイトではどのページに行っても、ヘッダーがあると思うので、ヘッダーを作成します

$ mkdir components
$ touch ./components/Header.tsx

Header.tsxの中身は下記です(タイトルをクリックするとトップページに戻れるようにリンクを設定しています)

Header.tsx
import Link from 'next/link'

export default function Header() {
  return (
    <header className="text-gray-600 body-font bg-blue-500">
      <div className="container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center">
        <Link href={'/'} passHref>
          <a className="flex title-font font-medium items-center text-gray-900 mb-4 md:mb-0">
            <span className="ml-3 text-xl text-white">My Blog</span>
          </a>
        </Link>
        <nav className="md:ml-auto flex flex-wrap items-center text-base justify-center">
          <a className="mr-5 text-white hover:text-gray-900">Profile</a>
        </nav>
      </div>
    </header>
  )
}

最後にヘッダー部分を全体のページに適用させるように_app.tsxを下記に修正

_app.tsx
import '../styles/globals.css';
import type { AppProps } from 'next/app';
+ import Header from '../components/Header';

function MyApp({ Component, pageProps }: AppProps) {
-  return <Component {...pageProps} />;
+  return (
+    <>
+      <Header />
+      <Component {...pageProps} />
+    </>
+  );
}

export default MyApp;

画像のようになっていれば成功です。
ヘッダー.png

Todoの作成

では、本題のTodoページを作成していきたいと思います。
最初にコンポーネント(部品)作っていこうと思います。
シンプルに2つの部品を作っていきます。

  • Todoのリスト
  • Todoを追加するためのフォーム

では、componentsディレクトリ直下にファイルを作成します。

$ touch ./components/{todoForm.tsx,todoList.tsx}

Todoのリストの方を下記にする
※画像は「./public/ハリネズミ.jpg」に配置する(画像名は自分で決めても問題ないです)

todoList.tsx
import type { Todo } from "../types/todolist"

type Props = {
  todolist: Todo
}

export default function TodoList( { todolist }: Props) {
  return (
    <>
      <div className="p-2 lg:w-1/3 md:w-1/2 w-full">
        <div className="h-full flex items-center border-gray-200 border p-4 rounded-lg">
          <img
            className="w-16 h-16 bg-gray-100 object-cover object-center flex-shrink-0 rounded-full mr-4"
            src="/ハリネズミ.jpeg"
            alt="icon"
          />
          <div className="flex-grow">
            <h2 className="text-gray-900 title-font font-medium">{ todolist.title}</h2>
            <p className="text-gray-500">{todolist.body}</p>
          </div>
        </div>
      </div>
    </>
  )
}

Todoを追加するためのフォームを下記にする

todoForm.tsx
export default function TodoForm( {handleadd} ) {
  return (
    <>
      <form className="font-sans text-sm rounded w-full max-w-md mx-auto my-8 px-8 pt-6 pb-8" onSubmit={handleadd}>
        <div className="relative border rounded mb-4 shadow appearance-none label-floating">
          <input className="w-full py-2 px-3 text-gray-700 leading-normal rounded" id="todotitle" type="text" placeholder="todotitle" name="todotitle" />
          <label className="absolute block text-gray-700 top-0 left-0 w-full px-3 py-2 leading-normal">
          </label>
        </div>
        <div className="flex items-center justify-between">
          <input type="submit" value="投稿" />
        </div>
      </form>
    </>
  )
}

次にTodoの構成を型宣言していく
新たにtypesディレクトリを作成し、todolist.tsを作成する

$ mkdir ./types
$ touch ./types/todolist.ts

todolist.tsの値を下記にする

todolist.ts
export type Todo = {
  id: number
  createdAt: string
  title: string
  body: string
  tag: string
}

pegesディレクトリ直下にtodoディレクトリを作成し、その直下にindex.tsxを作成します。

$ mkdir ./pages/todo
$ touch ./pages/todo/index.tsx

index.tsxを下記にする

index.tsx
import TodoList from "../../components/todoList"
import type { Todo } from "../../types/todolist"
import TodoForm from "../../components/todoForm"
import React, { useState } from "react"

type Props = {
  todolists: Array<Todo>
}

export default function Index({ todolists }: Props) {

  const [todos, settodo] = useState(todolists)

  const handle_add = async (event:any)=>{
    event.preventDefault()
    const data = {
      id: todos.length + 1,
      createdAt: "2022-02-02",
      title: event.target.todotitle.value,
      body: "テストテスト",
      tag: "特になし"
    }
    settodo([ ...todos, data])
    console.log(todolists)
    event.target.todotitle.value=''
  
  }
  return (
    <>
      <section className="text-gray-600 body-font">
        <div className="container px-5 py-24 mx-auto">
          <TodoForm  handleadd = {handle_add} />
          <div className="flex flex-col text-center w-full mb-20">
            <h1 className="sm:text-3xl text-2xl font-medium title-font mb-4 text-gray-900">Todo</h1>
          </div>
          <div className="flex flex-wrap -m-2">
            { todos.map(todolist =>(
              <TodoList todolist={todolist}></TodoList>
            ))}
          </div>
        </div>
      </section>
    </>
  )
}

export const getStaticProps = async () => {
  const url = process.env.JSON_FILE
  const req = await fetch( url )
  const json = await req.json()

  return {
    props: {
      todolists: json
    }
  }
}

最後にTodoの内容を記載したjsonファイルを配置し、envファイルを修正する
下記ディレクトリにファイルを配置

$ touch ./public/todo.json

jsonファイルを下記にする

todo.json
[
  { "id": 1,
    "created_at": "2022-02-11T15:59:15.693Z",
    "title": "todo1",
    "body": "テストです1",
    "tag": "特になし"
  },
  { "id": 2,
    "created_at": "2022-02-11T15:59:15.693Z",
    "title": "todo1",
    "body": "テストです2",
    "tag": "特になし"
  },
  { "id": 3,
    "created_at": "2022-02-11T15:59:15.693Z",
    "title": "todo1",
    "body": "テストです3",
    "tag": "特になし"
  }
]

envファイルを作成し、内容を記載する(envファイルとは環境変数を入れるファイルのこと)
※3000のところは設定したポート番号を記載してください。

$ touch ./public/.env.local
env.local
JSON_FILE=http://localhost:3000/todo.json

Todoページにアクセス

画像のようになっていれば成功です
todoページトップ.png

テキストボックスに「テスト」と入力して「投稿」をクリックするとtodoが追加される
todo追加.png

まとめ

今回ですが、簡単なTodoサイトを作成しました。
jsonのところをデータベースから取得するようにすれば書き換え可能なTodoサイトを作成できます。
TypeScriptで書いてるのですが、所々型宣言が抜けているところがあるため、学習を今後も続けていきたいです。
普段はPHP,Rubyを書いてることが多いですが、TypeScriptも楽しいですね〜
トレンドであるgolangとの連携もやってみたい。

2
3
1

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
2
3