1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Reactで作る簡単TODOアプリ

Posted at

1.Reactプロジェクトの作成

まずは、Reactプロジェクトを作成します。”vite”コマンドを実行して、Reactアプリをセットアップします。

プロジェクト名は「my-todo-app」です。

npm create vite@latest

> npx
> create-vite
|
o  Project name:
|  my-todo-app
|

プロジェクト名を入力すると、次はフレームワークとバリアントを選択します。

ここでは下記のように選択します。

o  Select a framework:
|  React
|
o  Select a variant:
|  TypeScript

cdコマンドで「my-todo-app」に移動して、「npm i」を実行します。

npm iは「package.json」に明示されているすべてのパッケージをインストールする際に使用

cd my-todo-app
npm i

devスクリプトを実行し、ローカルホストに接続します。

npm run dev
 VITE v6.2.3  ready in 1429 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

このような画面が出てきたら、Reactの実行環境の構築は完成です。
image.png

2.shadcn/uiのインストール

https://ui.shadcn.com/docs/installation/vite
を開いてtailwindcssをinstallするコマンドをコピーし、実行します。

npm install tailwindcss @tailwindcss/vite

src/index.cssに下記を追加します。

@import "tailwindcss";

インストールが完了したら、vscodeを開きます。

code .

vscode上でターミナルを開いて下記を実行します。

npm run dev

tsconfig.jsonを開いて色付けされている部分を追記します。
image.png

tsconfig.json
{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ],
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

tsconfig.app.jsonに色付けされている部分を追記します。

tsconfig.app.json
{
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "isolatedModules": true,
    "moduleDetection": "force",
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true,
    
    "baseUrl": ".",
    "paths": {
      "@/*": [
        "./src/*"
      ]
    }
  },
  "include": ["src"]
}

そして下記コマンドを実行します。

npm install -D @types/node

vite.config.jsonを下記のように変更。

vite.config.js
import path from "path"
import tailwindcss from "@tailwindcss/vite"
import react from "@vitejs/plugin-react"
import { defineConfig } from "vite"

// https://vite.dev/config/
export default defineConfig({
  plugins: [react(), tailwindcss()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
})

shadcn/uiの初期化します。base colorを聞かれますが、ここではSlateにします。

npx shadcn@latest init
...
Which color would you like to use as the base color? » Slate

さて初期化は完了しました。とりあえずボタンコンポーネントをインストールしてみましょう。shadcn/uiは必要なコンポーネントをインストールする必要があります。

npx shadcn@latest add button

そしてsrc/App.tsxの内容を下記に変更します。

App.tsx
import { Button } from "@/components/ui/button"

function App() {
  return (
    <div className="flex flex-col items-center justify-center min-h-svh">
      <Button>Click me</Button>
    </div>
  )
}

export default App

ローカルホストを開くとボタンが表示されています。

3.ToDoアプリの作成

ToDoカードの作成

新しいコンポーネントをインストールします。
今回はCardコンポーネントにします。

npx shadcn@latest add card

そしてApp.tsxを下記のようにします。

App.tsx
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./components/ui/card";
import { todo } from "node:test";

export default function Home() {
  const todos = ["Reactを学ぶ", "TypeScriptを学ぶ", "Viteを学ぶ"];
  return (
    <div className="flex justify-center items-center h-screen">
      <Card>
        <CardHeader>
          <CardTitle>TODO APP</CardTitle>
        </CardHeader>
        <CardContent>
          <ul>
            {todos.map((todo) => (
              <li>{todo}</li>
            ))}
          </ul>
        </CardContent>
        <CardFooter>
          <p>Card Footer</p>
        </CardFooter>
      </Card>
    </div>
  );
}

デフォルトではbodyスタイルにflexがついてしまっているので、bodyのスタイルからflexは削除しましょう。

index.css
body {
  margin: 0;
  place-items: center;
  min-width: 320px;
  min-height: 100vh;
}

ボタンの追加&ToDoの追加

App.tsx
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./components/ui/card";
import { todo } from "node:test";
import { useState } from "react";

export default function Home() {
  // const todos = ["Reactを学ぶ", "TypeScriptを学ぶ", "Viteを学ぶ"];
  const [todos, setTodos] = useState<string[]>([]);
  const [todo, setTodo] = useState("");
  return (
    <div className="flex justify-center items-center h-screen">
      <Card className="w-[400px]">
        <CardHeader>
          <CardTitle>TODO APP</CardTitle>
        </CardHeader>
        <CardContent>
          <div className="space-y-2">
            <input className="block w-full" placeholder="内容を追加" onChange={
              (e) => setTodo(e.target.value)}
              value={todo}
            />
          <Button className="w-full" 
          onClick={() => {
            setTodos([...todos, todo]);
            // setTodos(todos) ※駄目パターン
            setTodo("");
          }}>追加</Button>
          </div>
          <ul className="mt-4">
            {todos.map((todo) => (
              <li>{todo}</li>
            ))}
          </ul>
        </CardContent>
      </Card>
    </div>
  );
}

駄目パターンというのは、useStateが更新されないパターンになります。
todosは配列であるため、上記のパターンだと外見だけ考えると同じ配列を
引数に入れているため更新されません。
なので、改めて展開したあとに新しい配列として引数に入れてあげる必要があります。

setTodos([...todos, todo]);

ToDoの削除

ToDoの削除機能を追加したいと思います。
reactのアイコンをボタンに使用して削除ボタンを作成したいので、
react-iconのインストールをします。

npm install react-icon

削除ボタンを実装したのが下記です。

App.tsx
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./components/ui/card";
import { todo } from "node:test";
import { useState } from "react";
import { MdDelete } from "react-icons/md";

export default function Home() {
  // const todos = ["Reactを学ぶ", "TypeScriptを学ぶ", "Viteを学ぶ"];
  const [todos, setTodos] = useState<string[]>([]);
  const [todo, setTodo] = useState("");
  return (
    <div className="flex justify-center items-center h-screen">
      <Card className="w-[400px]">
        <CardHeader>
          <CardTitle>TODO APP</CardTitle>
        </CardHeader>
        <CardContent>
          <div className="space-y-2">
            <input className="block w-full" placeholder="内容を追加" onChange={
              (e) => setTodo(e.target.value)}
              value={todo}
            />
          <Button className="w-full" 
          onClick={() => {
            setTodos([...todos, todo]);
            // setTodos(todos) ※駄目パターン
            setTodo("");
          }}>追加</Button>
          </div>
          <ul className="mt-4">
            {todos.map((todo, index) => (
              <div className="flex">
                <li>{todo}</li>
                <button 
                  className="ml-3" 
                  onClick={() => {
                    setTodos(todos.filter((_, i) => i !== index));
                  }}
                >
                  <MdDelete className="h-3 w-3" color="red" />
                </button>
              </div>
            ))}
          </ul>
        </CardContent>
      </Card>
    </div>
  );
}

4.ToDoアプリのデプロイ

Firebaseを使用してデプロイをします。
※すみません、Firebaseへのアプリ登録まではスクショを取り忘れたので
ネットで調べてください

下記のコマンドを実行。

npm install firebase

Firebase CLIのインストールをします。

npm install -g firebase-tools

インストールが完了したら、Firebaseにログインと初期化をしていきます。

firebase login

firebase init hosting

? Please select an option: Use an existing project
? Select a default Firebase project for this directory: my-todo-app-3e59d (my-todo-app)
i  Using project my-todo-app-3e59d (my-todo-app)

=== Hosting Setup

Your public directory is the folder (relative to your project directory) that
will contain Hosting assets to be uploaded with firebase deploy. If you
have a build process for your assets, use your build's output directory.

? What do you want to use as your public directory? public
? Configure as a single-page app (rewrite all urls to /index.html)? No
? Set up automatic builds and deploys with GitHub? No
+  Wrote public/404.html
+  Wrote public/index.html

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...

+  Firebase initialization complete!

ではデプロイをします。

firebase deploy

=== Deploying to 'my-todo-app-3e59d'...

i  deploying hosting
i  hosting[my-todo-app-3e59d]: beginning deploy...
i  hosting[my-todo-app-3e59d]: found 5 files in dist
+  hosting[my-todo-app-3e59d]: file upload complete
i  hosting[my-todo-app-3e59d]: finalizing version...
+  hosting[my-todo-app-3e59d]: version finalized
i  hosting[my-todo-app-3e59d]: releasing new version...
+  hosting[my-todo-app-3e59d]: release complete

+  Deploy complete!

Project Console: https://console.firebase.google.com/project/my-todo-app-3e59d/overview
Hosting URL: https://my-todo-app-3e59d.web.app
firebase.json
{
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}

Hosting URLを検索すると、TODOアプリが表示されました!
image.png

お疲れ様でした。
以下、参考にした動画になります。
https://www.youtube.com/watch?v=LrwTuMTyxJM&t=484s

1
1
0

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?