初めに
今回、久しぶりにプログラミングの勉強をしようということで流行りのReactを勉強しました。
どうせならNext.jsも使って簡単なものを作ろうと思い、今回はTodoアプリを作成しました。
環境
- Docker
- Next.js
- TypeScript
- TailwindCSS
Dockerでの環境構築
ネットでたくさん出回っていますが、私は下記サイトを参考にしました。
https://zenn.dev/tasuya/articles/da033574b85e6d
ヘッダーを追加する
ほとんどのサイトではどのページに行っても、ヘッダーがあると思うので、ヘッダーを作成します
$ mkdir components
$ touch ./components/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を下記に修正
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;
Todoの作成
では、本題のTodoページを作成していきたいと思います。
最初にコンポーネント(部品)作っていこうと思います。
シンプルに2つの部品を作っていきます。
- Todoのリスト
- Todoを追加するためのフォーム
では、componentsディレクトリ直下にファイルを作成します。
$ touch ./components/{todoForm.tsx,todoList.tsx}
Todoのリストの方を下記にする
※画像は「./public/ハリネズミ.jpg」に配置する(画像名は自分で決めても問題ないです)
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を追加するためのフォームを下記にする
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の値を下記にする
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を下記にする
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ファイルを下記にする
[
{ "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
JSON_FILE=http://localhost:3000/todo.json
Todoページにアクセス
テキストボックスに「テスト」と入力して「投稿」をクリックするとtodoが追加される
まとめ
今回ですが、簡単なTodoサイトを作成しました。
jsonのところをデータベースから取得するようにすれば書き換え可能なTodoサイトを作成できます。
TypeScriptで書いてるのですが、所々型宣言が抜けているところがあるため、学習を今後も続けていきたいです。
普段はPHP,Rubyを書いてることが多いですが、TypeScriptも楽しいですね〜
トレンドであるgolangとの連携もやってみたい。