1. ð ã¯ããã«
5åã«ããã£ãŠããã³ããšã³ãéçºã®åºç€ãåŠãã§ããŸããã
æçµå㯠ãããŸã§ã®æ¯ãè¿ãã»Next.jsãžã®æ©æž¡ãã»ãã¹ããã©ã¯ãã£ã¹ã»ããããã®åŠã³æ¹ ããå±ãããŸãïŒ
2. ð ã·ãªãŒãºç·ãŸãšã
6åã§åŠãã å å®¹ãæ¯ãè¿ããŸãããã
| å | ããŒã | éèŠãã€ã³ã |
|---|---|---|
| 第äžå | HTMLåºç€ | ã¿ã°ã»ã»ãã³ãã£ãã¯HTMLã»å±æ§ |
| 第äºå | CSSåºç€ | ããã¯ã¹ã¢ãã«ã»Flexboxã»Gridã»ã¬ã¹ãã³ã·ãã»Tailwind |
| 第äžå | TypeScript DOMæäœ | DOMååŸã»ã€ãã³ãåŠçã»åå®å šãªæäœ |
| 第åå | Reactåºç€ | ã³ã³ããŒãã³ãã»Propsã»Stateã»useState |
| 第äºå | Reactå®è·µ | useEffectã»API飿ºã»ãã©ãŒã ã»ã³ã³ããŒãã³ãåå² |
| 第å å | ãŸãšãã»æ¬¡ã®ã¹ããã | Next.jsã»ãã¹ããã©ã¯ãã£ã¹ã»ããŒãããã |
ããã³ããšã³ãéçºã®å šäœå
3. ð Next.jsãšã¯ïŒïŒReactã®æ¬¡ã®ã¹ããã
Reactã ãã§ã¯è¶³ããªãããš
Reactã¯çŽ æŽãããã©ã€ãã©ãªã§ããããã®ãŸãŸã§ã¯äžè¶³ããŠããããšããããŸãã
| èª²é¡ | 説æ |
|---|---|
| ð ããŒãžé·ç§» | Reactã ãã§ã¯ããŒãžç§»åã®ä»çµã¿ããªã |
| ð SEO | Reactã¯ãã©ãŠã¶ã§ã¬ã³ããªã³ã°ããããSEOã«åŒ±ã |
| â¡ åæè¡šç€ºã®é床 | ããŒã¿ãååŸããŠãã衚瀺ãããããæåã®è¡šç€ºãé ã |
| ð ïž èšå®ã®è€éã | Webpackãªã©ã®ãã«ãããŒã«ããŒãããèšå®ããã®ãå€§å€ |
Next.jsã解決ããŠãããããš
Next.js ãšã¯ãReactãããŒã¹ã«ããæ¬çªåãã®Webãã¬ãŒã ã¯ãŒã¯ãã§ãã
Reactã®æ©èœããã®ãŸãŸäœ¿ããªãããäžèšã®èª²é¡ããã¹ãŠè§£æ±ºããŠãããŸãã
ReactïŒ ãUIãäœãã©ã€ãã©ãªã
Next.jsïŒ ãReactã§Webã¢ããªå
šäœãäœããã¬ãŒã ã¯ãŒã¯ã
äŸãïŒ
React = ãšã³ãžã³ïŒååïŒ
Next.js = è»å
šäœïŒãšã³ãžã³ + ãã㣠+ ãã³ãã« + ããïŒ
Next.jsã®äž»ãªæ©èœ
| æ©èœ | 説æ |
|---|---|
| ð App Router | ãã¡ã€ã«ã眮ãã ãã§ããŒãžãäœãã |
| ð¥ïž SSRïŒãµãŒããŒãµã€ãã¬ã³ããªã³ã°ïŒ | ãµãŒããŒåŽã§HTMLãçæããŠSEOå¯Ÿå¿ |
| â¡ SSGïŒéçãµã€ãçæïŒ | ãã«ãæã«HTMLãçæããŠé«é衚瀺 |
| ð API Routes | ããã³ããšã³ããšåããããžã§ã¯ãã§APIãäœãã |
| ðŒïž Imageæé©å | ç»åãèªåã§æé©åããŠè¡šç€ºãé«éå |
Vite vs Next.js ã®äœ¿ãåã
| Vite + React | Next.js | |
|---|---|---|
| åããŠããçšé | 管çç»é¢ã»SPAã¢ã㪠| äžè¬å ¬éWebãµã€ãã»ããã° |
| SEO | â 匱ã | â 匷ã |
| ããŒãžé·ç§» | èŠã©ã€ãã©ãªïŒReact RouterïŒ | â æšæºæèŒ |
| åŠç¿ã³ã¹ã | äœã | äžçšåºŠ |
| æåã®äžæ© | â åŠç¿ã«æé© | Next.jsãåŠã¶åã«Reactãçè§£ããŠãã |
ð¡ ãã®ã·ãªãŒãºã§åŠãã Reactã®ç¥èã¯Next.jsã§ãã®ãŸãŸäœ¿ããŸãïŒ
ã³ã³ããŒãã³ãã»Propsã»Stateã»useEffectã¯ãŸã£ããåãã§ããâ ïž Next.jsïŒApp RouterïŒã䜿ããšãã®æåã®å£ïŒ
"use client"
Next.jsã®App Routerã§ã¯ãã³ã³ããŒãã³ãã¯ããã©ã«ãã§ãµãŒããŒåŽã§åãïŒServer ComponentsïŒ ãããuseStateãuseEffectããã®ãŸãŸæžããšä»¥äžã®ãšã©ãŒãåºãŸããError: useState can only be used in a Client Component. Add the "use client" directive at the top of the file.解決çïŒãã¡ã€ã«ã®å é ã«
"use client"ã远å ãã"use client"; // â ããã1è¡è¿œå ããã ãã§OKïŒ import { useState } from "react"; const Counter = () => { const [count, setCount] = useState(0); return <button onClick={() => setCount(count + 1)}>{count}</button>; };
"use client"ã¯ããã®ã³ã³ããŒãã³ãã¯ãã©ãŠã¶åŽã§åããããšãã宣èšã§ãã
useStateã»useEffectã»ã€ãã³ãåŠçã䜿ãã³ã³ããŒãã³ãã«ã¯å¿ ã远å ããŸãããã
Next.jsã®ãããžã§ã¯ãäœæ
# Next.js + TypeScript ã®ãããžã§ã¯ããäœæ
npx create-next-app@latest my-app --typescript --tailwind --app
# ãããžã§ã¯ããã©ã«ãã«ç§»å
cd my-app
# éçºãµãŒããŒãèµ·å
npm run dev
App Routerã®ãã¡ã€ã«æ§é
my-app/
âââ app/ â ããŒãžã»ã¬ã€ã¢ãŠã
â âââ layout.tsx â å
šããŒãžå
±éã®ã¬ã€ã¢ãŠã
â âââ page.tsx â ãããããŒãžïŒ/ïŒ
â âââ about/
â â âââ page.tsx â /about ããŒãž
â âââ users/
â âââ page.tsx â /users ããŒãžïŒãŠãŒã¶ãŒäžèЧïŒ
â âââ [id]/
â âââ page.tsx â /users/1 ã®ãããªåçããŒãž
âââ components/ â åå©çšã³ã³ããŒãã³ã
âââ types/ â åå®çŸ©
âââ public/ â ç»åãªã©ã®éçãã¡ã€ã«
4. â React à TypeScript ã®ãã¹ããã©ã¯ãã£ã¹
â ã³ã³ããŒãã³ãã®åœåèŠå
// â
ã³ã³ããŒãã³ãåã¯ãã¹ã«ã«ã±ãŒã¹ïŒå€§æåå§ãŸãïŒ
const UserCard = () => { ... };
const NavigationMenu = () => { ... };
// â
ãã¡ã€ã«åããã¹ã«ã«ã±ãŒã¹
// UserCard.tsx
// NavigationMenu.tsx
// â å°æåã»ãã£ã¡ã«ã±ãŒã¹ã¯NG
const userCard = () => { ... }; // ã³ã³ããŒãã³ããšé¢æ°ã®åºå¥ãã€ããªã
â¡ Propsã®å㯠type ã§å®çŸ©ãã
ð¡
typeãšinterfaceã©ã¡ãã䜿ãïŒ
第ååã§ã¯interfaceã䜿ã£ãæžãæ¹ã玹ä»ããŸããããçŸä»£ã®Reactéçºã§ã¯æ¡åŒµæ§ãæè»ãªtypeã䜿ãã¹ã¿ã€ã«ãå¢ããŠããŸãã
ã©ã¡ãã䜿ã£ãŠãåäœã¯åãã§ãã奜ã¿ãããŒã ã®ã«ãŒã«ã§éžãã§OKã§ãïŒ
// â
çŸä»£ã¹ã¿ã€ã«ïŒtype ã§å®çŸ©
type ButtonProps = {
label: string;
onClick: () => void;
variant?: "primary" | "secondary"; // Unionåã§ãšãåŸãå€ãå¶éã§ãã
};
const Button = ({ label, onClick, variant = "primary" }: ButtonProps) => {
return (
<button
onClick={onClick}
className={variant === "primary" ? "btn-primary" : "btn-secondary"}
>
{label}
</button>
);
};
⢠ã€ãã³ããã³ãã©ãŒã®åœå
// â
on + åè© ã®åœ¢ã§åœåããïŒReactã®æ
£ç¿ïŒ
type CardProps = {
onClose: () => void; // éãããšã
onSubmit: () => void; // éä¿¡ãããšã
onDelete: (id: number) => void; // åé€ãããšã
};
// â
颿°ã®å®çŸ©ã¯ handle + åè©
const handleClose = () => { ... };
const handleSubmit = () => { ... };
const handleDelete = (id: number) => { ... };
// Props ã«æž¡ããšã
<Card onClose={handleClose} onSubmit={handleSubmit} />
⣠æ¡ä»¶ä»ãã¬ã³ããªã³ã°ã®ãã¿ãŒã³
const Example = () => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [user, setUser] = useState<User | null>(null);
const [items, setItems] = useState<string[]>([]);
return (
<div>
{/* â && æŒç®åïŒæ¡ä»¶ãtrueã®ãšãã ã衚瀺 */}
{isLoggedIn && <p>ãã°ã€ã³äžã§ã</p>}
{/* â¡ äžé
æŒç®åïŒtrueãªãLogoutButtonãfalseãªãLoginButton */}
{isLoggedIn ? <LogoutButton /> : <LoginButton />}
{/* ⢠ãªãã·ã§ãã«ãã§ãŒã³ïŒnullãããããªãå€ãå®å
šã«è¡šç€º */}
{user?.name && <p>ããã«ã¡ã¯ã{user.name}ãã</p>}
{/* ⣠é
åã空ã®ãšãã®ãã©ãŒã«ãã㯠*/}
{items.length > 0 ? (
<ul>{items.map((item) => <li key={item}>{item}</li>)}</ul>
) : (
<p>ã¢ã€ãã ããããŸãã</p>
)}
</div>
);
};
†ã«ã¹ã¿ã ããã¯ïŒããžãã¯ãåé¢ãã
ã«ã¹ã¿ã ããã¯ãšã¯ã
useãããšããååã§å§ãŸãèªåã§äœã颿°ãã§ãã
è€æ°ã®ã³ã³ããŒãã³ãã§äœ¿ããããŒã¿ååŸããç¶æ 管çããªã©ã®ããžãã¯ã1ã€ã®é¢æ°ã«ãŸãšããŠåå©çšã§ããããã«ããŸãã
// â
ããŒã¿ååŸã®ããžãã¯ãã«ã¹ã¿ã ããã¯ã«åãåºã
// src/hooks/usePosts.ts
import { useState, useEffect } from "react";
// APIããååŸããããŒã¿ã®å
type Post = {
id: number;
title: string;
body: string;
};
// ãã®ããã¯ããäœãè¿ããããå
ã«åã§å®çŸ©ããŠãã
type UsePostsReturn = {
posts: Post[]; // ååŸããæçš¿äžèЧ
isLoading: boolean; // èªã¿èŸŒã¿äžãã©ãã
error: string | null; // ãšã©ãŒã¡ãã»ãŒãžïŒãªããã°nullïŒ
};
// ã«ã¹ã¿ã ããã¯ã¯å¿
ããuseãããå§ããååã«ããïŒReactã®ã«ãŒã«ïŒ
const usePosts = (): UsePostsReturn => {
// ãã®ããã¯ã®äžã§äœ¿ãStateïŒæ®éã®ã³ã³ããŒãã³ããšåãæžãæ¹ïŒ
const [posts, setPosts] = useState<Post[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// ããŒã¿ååŸã®ããžãã¯ïŒç¬¬äºåã§åŠãã useEffectãšåãïŒ
useEffect(() => {
const fetchPosts = async () => {
try {
// APIããããŒã¿ãååŸãã
const response = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=5");
const data: Post[] = await response.json();
setPosts(data); // ååŸããããŒã¿ãStateã«ä¿å
} catch (e) {
// ãšã©ãŒãèµ·ããããšã©ãŒã¡ãã»ãŒãžãã»ãã
setError("ååŸã«å€±æããŸãã");
} finally {
// æåã»å€±æã©ã¡ãã§ãèªã¿èŸŒã¿å®äºã«ãã
setIsLoading(false);
}
};
fetchPosts(); // å®çŸ©ãã颿°ãå®è¡ãã
}, []); // [] = ã³ã³ããŒãã³ãã衚瀺ããããšãã«1åã ãå®è¡
// ãã®ããã¯ã䜿ãåŽïŒã³ã³ããŒãã³ãïŒã«3ã€ã®å€ãè¿ã
return { posts, isLoading, error };
};
export default usePosts;
// ã³ã³ããŒãã³ããã¹ãããªããïŒ
// src/components/PostList.tsx
import usePosts from "../hooks/usePosts";
const PostList = () => {
// â é·ãuseStateã»useEffectã®ã³ãŒããã1è¡ãã«ãªã£ãïŒ
const { posts, isLoading, error } = usePosts();
// èªã¿èŸŒã¿äžã¯ããŒãã£ã³ã°è¡šç€º
if (isLoading) return <p>èªã¿èŸŒã¿äž...</p>;
// ãšã©ãŒãããã°ãšã©ãŒã¡ãã»ãŒãžã衚瀺
if (error) return <p>ãšã©ãŒïŒ{error}</p>;
// ååŸããããŒã¿ãäžèŠ§è¡šç€º
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
};
ð¡ ã«ã¹ã¿ã ããã¯ã®ã¡ãªããïŒ
- åãããŒã¿ååŸããžãã¯ãå¥ã®ã³ã³ããŒãã³ãã§ã
usePosts()ã®1è¡ã§äœ¿ãåãã- ã³ã³ããŒãã³ãã¯ã衚瀺ããããšãã ãã«éäžã§ãã
- ãã¹ãããããã°ããããããªãïŒããžãã¯éšåã ã確èªã§ããïŒ
5. ðºïž åŠç¿ããŒãããã
ãã®ã·ãªãŒãºãçµããããšã®åŠã³æ¹
ã¹ãããå¥ã®åŠç¿å 容
ð± STEP 1ïŒåºç€åºãïŒãã®ã·ãªãŒãºå®äºåŸïŒ
| ããããš | ç®ç |
|---|---|
| å°ããªã¢ããªãèªåã§äœã | Todoãªã¹ããã¡ã¢ã¢ããªããŒãããäœã£ãŠã¿ã |
| TypeScriptã®åã«æ £ãã | ãšã©ãŒãèªãã§ä¿®æ£ããçµéšãç©ã |
| ã³ã³ããŒãã³ãèšèšãç·Žç¿ | ãã©ãåããã°ãããããèããç¿æ £ |
ð¿ STEP 2ïŒNext.jsãžç§»è¡ïŒ1ã2ã¶æåŸïŒ
| ããããš | ç®ç |
|---|---|
| Next.jsã®å ¬åŒãã¥ãŒããªã¢ã« | App Routerã®åºæ¬ãåŠã¶ |
| ããŒãžé·ç§»ã»åçã«ãŒãã£ã³ã° | è€æ°ããŒãžã®ã¢ããªãäœããããã«ãªã |
| Supabaseãšé£æº | å®éã®ããŒã¿ããŒã¹ã䜿ã£ãã¢ããªãäœã |
ð³ STEP 3ïŒå®è·µã¬ãã«ïŒ3ã6ã¶æåŸïŒ
| ããããš | ç®ç |
|---|---|
| èªèšŒæ©èœãå®è£ | ãã°ã€ã³ã»ãŠãŒã¶ãŒç®¡çãäœããããã«ãªã |
| Vercelã§ããã〠| å®éã«ã€ã³ã¿ãŒãããã§å ¬éã§ããããã«ãªã |
| OSSãèªãã»ã³ã³ããªãã¥ãŒã | å®éã®ã³ãŒãããåŠã¶ |
åèã«ãªããªãœãŒã¹
| ãªãœãŒã¹ | å 容 | URL |
|---|---|---|
| Reactå ¬åŒããã¥ã¡ã³ã | Reactã®åºç€ããå¿çšãŸã§ïŒæ¥æ¬èªããïŒ | react.dev |
| Next.jså ¬åŒããã¥ã¡ã³ã | Next.jsã®å šæ©èœ | nextjs.org |
| TypeScriptå ¬åŒ | TypeScriptã®ãã¹ãŠ | typescriptlang.org |
| Tailwindå ¬åŒ | ã¯ã©ã¹åã®äžèЧ | tailwindcss.com |
| shadcn/uiå ¬åŒ | ã³ã³ããŒãã³ãã®äœ¿ãæ¹ | ui.shadcn.com |
6. ð¡ ããã³ããšã³ãéçºã§å€§åãªèãæ¹
â ãŸãåããã»ããšã§ç¶ºéºã«ãã
â å®ç§ãªã³ãŒããæåããæžãããšãã
â ãªããªã宿ããªãã»æ«æãããã
â
ãŸãåããã®ãäœã â å°ããã€æ¹åãã
â æ©ã宿ã§ããã»æ¹åç¹ãèŠãããã
â¡ ãšã©ãŒã¯åé
ãšã©ãŒã¡ãã»ãŒãž = ããããåé¡ã§ããïŒããšãããã³ã
â
ãšã©ãŒã¡ãã»ãŒãžãããèªã
â
ãšã©ãŒæããã®ãŸãŸæ€çŽ¢ãã
â
AIã«ããã®ãšã©ãŒã®æå³ãšè§£æ±ºæ¹æ³ãæããŠããšèã
â¢ å ¬åŒããã¥ã¡ã³ããèªãç¿æ £
ããã°èšäºã»Qiitaèšäº â ãããããããå€ãããšã
å
¬åŒããã¥ã¡ã³ã â ææ°ã»æ£ç¢ºã»ä¿¡é Œã§ãã
ãŸãã¯å
¬åŒããã¥ã¡ã³ãã確èªããç¿æ
£ãïŒ
7. ð¯ æåŸã«
ãããã³ããšã³ãã¯å¥¥ãæ·±ããã§ãæåã®äžæ©ã¯å°ãããŠãããã
ãã®ã·ãªãŒãºãæåŸãŸã§èªãã§ãããããªããžã
HTMLã»CSSã»TypeScriptã»Reactãšãããããã®æè¡ãåŠãã§ããŸããã
æåã¯é£ããæããããšããæãåãããã¡ã«å°ããã€æèŠãã€ãããŠããã¯ãã§ããæ¬¡ã®äžæ©ãšããŠããŸãäœã1ã€äœã£ãŠã¿ãŠãã ããã
- ToDoãªã¹ãã§ã
- èªå·±ç޹ä»ããŒãžã§ã
- 奜ããªAPIã䜿ã£ãã¢ããªã§ã
ãäœããããã®ãäœããããæéã®åŠç¿æ³ã§ãð
ð¬ 質åãææ³ãããã°ãã³ã¡ã³ãæ¬ã§ãæ°è»œã«ã©ãã!
ð 圹ã«ç«ã£ãããããã&ã¹ããã¯ããé¡ãããŸã!
ð ãããŸã§èªãã§ãã ãã£ãŠãæ¬åœã«ããããšãããããŸãã!
ð ã·ãªãŒãºèšäº
- ã第äžåãWebããŒãžã®ä»çµã¿ã»HTMLåºç€
- ã第äºåãCSSã§èŠãç®ãæŽãã
- ã第äžåãTypeScriptã§ã€ã³ã¿ã©ã¯ã·ã§ã³ãäœã
- ã第ååãReact à TypeScriptã®åºç€
- ã第äºåãReact à TypeScriptã®å®è·µ
- ã第å åããŸãšãã»Next.jsã»ãã¹ããã©ã¯ãã£ã¹ã»åŠç¿ããŒããããïŒãã®èšäºïŒ