LoginSignup
3
1

NEXTJS13の基本機能まとめ(Routing, Data Fetching, Server components)

Last updated at Posted at 2023-09-02

はじめに

本記事は、以下のNEXTJSのチュートリアルの内容をまとめたものである。

目次

  • 事前準備
  • ルーティングについて
  • 全ページ表示させるコンポーネントの配置先
  • API呼び出しの方法について
  • Server Side Renderingの特徴
  • どちらのコンポーネントで実装するか 

事前準備

プロジェクトの作成

$ npm create next-app

以下で実行できることを確認

$ npm run dev

ルーティングについて

基本形

src/appフォルダ配下に任意の名称のフォルダを作成し、そのファイル名をpage.tsxとする

これだけで、/フォルダ名 のページにルーティングできるようになる。
/about というリソースを作成する場合は以下のようにする。

src/app/a¬bout/page.tsx
 export default function About(){
    return (
        <div><h1> This is about page.</h1></div>
    );

ネストしたroute

src/app/about/us/page.tsx
 export default function AboutUs(){
    return (
        <div><h1> This is about us page.</h1></div>
    );

dynamic route

src/app/about/[id]/page.tsx
export default function AboutWithId({ params }: any) {
  return (
    <div>
      <h1> This is the about page with ID: {params.id} </h1>
    </div>
  );
}

全ページ共通で表示させるコンポーネントの配置

  • Navbar
  • Footer
    など

Navbarをつけたい場合

src/app/components/navbar.tsx
import Link from "next/link";

export default function Navbar() {
  return (
    <div>
      <Link href="/about"> About </Link>
      <Link href="/about/us"> About Us</Link>
    </div>
  );
}

デフォルトで作成されている以下でrouteが管理されている。
{children}が、それぞれのpageの中身なので、その上にNavbarを入れればいい。

src/app/layout.tsx

import "./globals.css";
import { Inter } from "next/font/google";
+ import Navbar from "./components/navbar";

const inter = Inter({ subsets: ["latin"] });

export const 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}>
+       <Navbar />
        {children}
      </body>
    </html>
  );
}



API呼び出しの方法について

api呼び出し(x1)

src/app/listofposts/pages.tsx

const getPostsData = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
  return res.json();
};

export default async function ListOfPosts() {
  const posts = await getPostsData();
  return (
    <div>
      {posts.map((post: any) => {
        return <p> {post.title} </p>;
      })}
    </div>
  );
}

api呼び出し(x2)

src/app/listofposts/pages.tsx

const getPostsData = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
  return res.json();
};

+ const getUsersData = async () => {
+  const res = await fetch("https://jsonplaceholder.typicode.com/users");
+  return res.json();
+ };

export default async function ListOfPosts() {
- const posts = await getPostsData();
+  const [posts, usres] = await Promise.all([getPostsData(), getUsersData()]);
  return (
    <div>
      {posts.map((post: any) => {
        return <p> {post.title} </p>;
      })}

+      {usres.map((user: any) => {
+        return <p> {user.name}</p>;
+      })}
    </div>
  );
}

Server Side Renderingはスタティックである

サーバーコンポーネントは、ブラウザのイベントやuseState hooksなどを使用できない。
また、基本的に最初のロード時にキャッシュされるため、中の要素はスタティックになる。

キャッシュの設定(時間設定なし)

リロード時に毎回APIを呼び出しフェッチしたい場合

src/app/listofposts/pages.tsx
import Image from "next/image";

const getPostsData = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
  return res.json();
};

const getUsersData = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/users");
  return res.json();
};

const getDogData = async () => {
-  const res = await fetch("https://dog.ceo/api/breeds/image/random");
+  const res = await fetch("https://dog.ceo/api/breeds/image/random", {
+    cache: "no-store",
+  });
  return res.json();
};

export default async function ListOfPosts() {

  const [posts, usres, dog] = await Promise.all([
    getPostsData(),
    getUsersData(),
    getDogData(),
  ]);
  return (
    <div>
      <Image src={dog.message} alt="dog" width={300} height={300} />
      {posts.map((post: any) => {
        return <p> {post.title} </p>;
      })}

      {usres.map((user: any) => {
        return <p> {user.name}</p>;
      })}
    </div>
  );
}

キャッシュの設定(時間設定あり)

指定秒数以上開けてリフレッシュされた場合に再取得したい場合
※以下例だと3秒以上開けた後ににリフレッシュした場合再フェッチされる

const getDogData = async () => {
  const res = await fetch("https://dog.ceo/api/breeds/image/random", {
    next: {
      revalidate: 3,
    },
  });
  return res.json();
};

他のウェブサイトやapiから画像を取得して表示させるためには、以下に取得を許可するドメインを設定する必要がある。

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
+  images: {
+    domains: ["images.dog.ceo"],
+  },
};

module.exports = nextConfig;

page以外の要素

error

サーバエラー発生時にクライアントにエラーを表示させたい場合に使用
※クライアント側のコンポーネントである必要があるため、"use client"が必要。

src/listofposts/error.tsx
+ "use client";

export default function Error() {
  return <div> There was an error loading the data.</div>;
}

loading

ページ読み込みの際に、「Loading...」のような表示をさせたい場合に使用

src/listofposts/loading.tsx

export default function Loading(){
  return <div> Loading...</div>;
}

目的の違い

クライアントコンポーネント
ユーザと実際のクライアント(ブラウザ)の間を取り持つ。そのため、ブラウザイベントや、React Hooks(ブラウザのステートなど)を管理する。
サーバーコンポーネント
コンポーネントをサーバにレンダリングすることで、クライアントに送信しロードするjavascriptの量を減らすことができる。

守るべきルール

サーバーコンポーネントをクライアントコンポーネントにインポートできない

サーバーコンポーネントをクライアントコンポーネントのpropsまたはchildrenとして渡せる

どちらのコンポーネントで実装するか

Client
React hooksを使う時
onClickなどのイベントとリスナーを使う時
stateやeffectsなどに依存するカスタムhooksを使う場合
Server
センシティブな情報を保持する必要がある時(tokens, API keys など)
バックエンドのリソースに直接アクセスする必要がある時
3
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
3
1