Next.jsの始め方
2024/6/5 2024/6/5 システム
はじめに
この記事の対象者
・今からNext.jsを始める方
・Next.js初心者の方
Node.jsとnpmのインストール
まず、Node.jsをnpmのインストールが必要となります。これれがインストールされていることを確認してください。これらはNode.jsの依存関係を管理するために必要となります。
node -v
npm -v
インストールされていない場合は、Node.jsの公式サイトからインストーラーをダウンロードしてインストールしてください。
新しいNext.jsプロジェクトの作成
「create-next-app」というコマンドを使用して新しくプロジェクトを作成します。
プロジェクトのセットアップを自動で行ってくれます。
以下のコマンドを実行して新しいプロジェクトを作成します。今回はnextjs-testというプロジェクトを作成します。
npx create-next-app@latest nextjs-test
インストールの際に「Would you like to use App Router? (recommended)」と聞かれますが、Yesを選択してください。後ほど説明致します。
プロジェクトが作成できたらプロジェクトのディレクトリに移動します。
cd nextjs-test
開発サーバーの起動
以下のコマンドを実行し、開発サーバーを起動してください。
npm run dev
これにてローカル開発サーバーが起動し、http://localhost:3000
でアクセスできます。
以下のような画面が表示されれば成功です。
作成されたプロジェクトのディレクトリ構造は以下のようになっています。
nextjs-test/
├── node_modules/
├── public/
│ ├── favicon.ico
│ └── vercel.svg
├── app/
│ ├── favicon,ico
│ ├── globals.css
│ ├── layout.tsx
│ ├── page.tsx
├── .gitignore
├── package.json
└── README.md
App Routerについて
先ほど「Would you like to use App Router? (recommended)」でYesを選択してくださいと記載しましたが、こちら2023年5月に公開されたバージョン13.4で従来はpagesというディレクトリにてルーティングをするようになっていました。pagesというディレクトリの配下に作成したいページのファイル(XXX.tsx)を作成するだけで自動でルーティングが設定されるというものでしたが、2023年5月以降はAppRouterが推奨されるようになりました。
appディレクトリの配下に作成したいページのディレクトリを作成し、さらにその配下にpage.tsxを作成することでPage Routerと同様にルーティングが設定されます。
app/
|- example1/
| page.tsx
|
|- example2/
| page.tsx
|...
では実際にTODOを管理できるWebアプリケーションを作ってみたいと思います。
appディレクトリ配下を実際に触ってみる
appディレクトリを見てみるとpage.tsxの他にlayout.tsxというファイルがあると思います。このファイルで各ページで共通するコンポーネントやスタイルをレイアウトとして定義することができます。各ページのディレクトリ配下に配置可能ですが、appディレクトリ直下では必ず配置する必要があります。layout.tsxは以下のようになっています。
import './globals.css'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
)
}
全体のスタイルを定義しているglobal.cssもここでimportしています。またmetadataの定義もここでしています。App Routerではメタ情報をmetadataオブジェクトをexportすることで設定しているようです。実際にmetadataオブジェクトのtitleを「Nextjs Test」に変更すると反映されます。
トップページも編集してみましょう。
page.tsxを以下のように編集します。
const Home: React.FC = () => {
return <h1>TOPページ</h1>;
};
export default Home;
このようになりました。このままではスタイルが適用されているためglobal.cssを以下のように編集し、背景色のみ適用させます。
html,
body {
background-color: #f0ffff;
}
反映されるとこのようになります。
ヘッダーの追加
layout.tsxでは各ページで共通するコンポーネントをレイアウトできると記載しましたので、ヘッダーを追加したいと思います。まずはHeaderコンポーネントを作成します。今回はコンポーネントはプロジェクト直下にcomponentsディレクトリを作成し、そこにまとめていきます。
nextjs-project/
|- app/
|...
|- components/
| |- Header/
| | index.tsx
| | header.module.css
|...
Headerコンポーネントは以下のようなものを作成しました。Webアプリケーションの名前を表示するだけの簡単なものです。
import styles from "./header.module.css";
import { useRouter } from "next/navigation";
export const Header: React.FC = () => {
const router = useRouter();
return (
<div className="headerArea">
<h1 className="headerText">
TEST
</h1>
</div>
);
};
ではlayout.tsxに追加してみましょう。
import { Header } from "@/components/Header";
・・・
<body className={inter.className}>
<header />
{children}
</html>
);
}
ページをリロードすると以下のようなエラーが発生します。
このエラーメッセージは”useRouter”フックがサーバーコンポーネント内で使用されていることを示しています。コンポーネントにはServer ComponentsとClient Componentsという概念があり、Server Componentsはサーバーサイドでレンダリングされ、Client Componentsはクライアントサイドでレンダリングされるコンポーネントです。Next.js 13ではデフォルトでコンポーネントは全てServer ComponentになっているためClient Componentsにするには宣言が必要となっています。
“useRouter”はClient Components内のみで使用できるため、該当コンポーネントまたはその親コンポーネントに「use client」ディレクティブを追加する必要があります。
Headerコンポーネントのファイル(components/Header/index.tsx)の先頭に「use client」ディレクティブを追加します。
"use client"; //これを追加
import styles from "./header.modules.css";
import { useRouter } from "next/navigation";
export const Header: React.FC = () => {
const router = useRouter();
return (
<div className="headerArea">
<h1 className="headerText">
TEST
</h1>
</div>
);
};
「use client」ディレクティブは、そのファイルがクライアント側で実行されるべきであることを示します。ディレクティブはファイルの最上部に配置します。
この変更を行った後、再度開発サーバーを起動して動作を確認してください。
npm run dev
エラーが解消されました。
Todoリストの実装
解説は後ほどとし、出来上がったソースがこちらです。TodoList.tsxとTodoItem.tsxはcomponents直下に配置してください。
page.txt
import React from 'react';
import TodoList from '../components/TodoList';
const Home = () => {
return (
<div>
<h1>TODO</h1>
<TodoList />
</div>
);
};
export default Home;
TodoList.tsx
"use client"
import React, { useState } from 'react';
import TodoItem from './TodoItem';
const TodoList = () => {
//タスクと新しいタスク入力を管理するためのuseState
const [tasks, setTasks] = useState<Array<{ task: string; completed: boolean }>>([]);
const [newTask, setNewTask] = useState('');
//タスク配列に新しいタスクを追加する関数
const addTask = () => {
//新しいタスク入力が空でないことを確認
if(newTask.trim()) {
//新しいタスクでタスク配列を更新
setTasks([...tasks, {task: newTask, completed: false}]);
//新しいタスク入力フィールドをリセット
setNewTask('');
}
};
//タスクを削除する
const removeTask = (index: number) => {
//タスクの配列のコピーを作成
const newTasks = [...tasks];
//タスク配列から指定されたタスクを削除
newTasks.splice(index, 1);
//タスク配列のステートを更新
setTasks(newTasks);
};
return (
<div>
{/* 新しいタスクを追加するための入力フィールド */}
<input
type="text"
placeholder="Add a task"
value={newTask}
onChange={(e) => setNewTask(e.target.value)}
/>
<button onClick={addTask}>追加</button>
{/* タスクのリストをレンダリング */}
<div>
{tasks.map((task, index) => (
<TodoItem
key={index}
task={task.task}
//removeTask関数をTodoImteに渡す
toggleCompletion={() => removeTask(index)}
/>
))}
</div>
</div>
);
};
export default TodoList;
TodoItem.tsx
import React from 'react';
const TodoItem = (props: {task: string; toggleCompletion: () => void; }) => {
return (
<div>
{/* タスクテキストを表示 */}
<span>{props.task}</span>
{/* toggleCompletion関数をトリガーするボタン */}
<button onClick={props.toggleCompletion}>
✔️
</button>
</div>
);
};
export default TodoItem;
このような画面になれば成功です。入力欄にタスクを入力して追加ボタンをクリックするとタスクが追加され、チェックボタンをクリックするとタスクが削除されます。
以下注意点です。
注意点1
TodoList.tsx
const [tasks, setTasks] = useState<Array<{ task: string; completed: boolean }>>([]);
ここはuseStateの宣言をする箇所です。Typescriptでは型の指定をしないといけないため配列と型を明示しておかないとエラーとなってしまいます。
注意点2
Todoリストのように何個も同じような要素を作成する場合、要素にkeyをつけてあげるようにしましょう。keyをつけないとReactが要素をそれぞれ判別できず追加したり削除するたびに全て際レンダリングすることとなってしまうようです。
注意点3
TodoItem.tsxでTodoItemにpropsとして関数を渡しています。ここでvoidと出てきますがこちらが「何も返さない」な返り値の型です。toggleCompletionにはremoveTask関数が入ってきてボタンをクリックしたらタスクを削除する関数が渡されています。このremoveTask関数は返り値が存在しないためtoggleCompletionには返り値が発生しない関数を渡すという意味でこのような書き方となっています。