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の実行環境の構築は完成です。
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を開いて色付けされている部分を追記します。
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
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を下記のように変更。
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の内容を下記に変更します。
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を下記のようにします。
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は削除しましょう。
body {
margin: 0;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
ボタンの追加&ToDoの追加
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
削除ボタンを実装したのが下記です。
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
{
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
Hosting URLを検索すると、TODOアプリが表示されました!
お疲れ様でした。
以下、参考にした動画になります。
https://www.youtube.com/watch?v=LrwTuMTyxJM&t=484s