■P297

app/page.js
────────────────
const Home = () => {
  console.log("レンダリング");
  return <h1>TOPページ</h1>;
};

export default Home;
────────────────


■P299

app/page.js
────────────────
"use client"

const Home = () => {
  console.log("レンダリング");
  return <h1>TOPページ</h1>;
};

export default Home;
────────────────


■P300

────────────────
import { useState } from "react";

const Home = () => {
  const [count] = useState(0);
  console.log(count);
  return <div>TOPページ</div>;
};

export default Home;
────────────────

────────────────
const Home = () => {
  return (
    <div onClick={() => console.log("クリック")}>
      TOPページ
    </div>
  );
};

export default Home;
────────────────


■P301

app/api/route.js
────────────────
const items = [
  {
    id: 1,
    name: "Next Book A",
    price: 4000,
  },
  {
    id: 2,
    name: "Next Book B",
    price: 5000,
  },
  {
    id: 3,
    name: "Next Book C",
    price: 3000,
  },
];

export const GET = async (request) => {
  await new Promise((resolve) => setTimeout(resolve, 2000));
  const { searchParams } = new URL(request.url);
  const id = searchParams.get("id");

  const item = items.find((item) => item.id === Number(id));

  if (!item) {
    return new Response("item not found", {
      status: 404,
    });
  }

  return Response.json(item);
};
────────────────


■P302

app/item/[id]/loading.js
────────────────
const Loading = () => {
  return <div>loading...</div>;
};

export default Loading;
────────────────


■P303

app/item/[id]/_StreamingPage.js
────────────────
import { notFound } from "next/navigation";

const StreamingPage = async ({ id }) => {
  const response = await fetch(`http://localhost:3000/api?id=${id}`);
  if (response.status === 404) {
    notFound();
  }
  const item = await response.json();
  return (
    <div>
      <h2>{item.name}</h2>
      <p>価格: {item.price}円</p>
    </div>
  );
};

export default StreamingPage;
────────────────


■P304

app/item/[id]/page.js
────────────────
import { Suspense } from "react";
import StreamingPage from "./_StreamingPage";

export const generateMetadata = async ({ params }) => {
  const { id } = await params;
  const response = await fetch(`http://localhost:3000/api?id=${id}`);
  const item = await response.json();
  return {
    title: item.name,
  };
};

const Item = async ({ params }) => {
  const { id } = await params;
  return (
    <Suspense fallback={<div>loading...</div>}>
      <StreamingPage id={id} />
    </Suspense>
  );
};

export default Item;
────────────────

────────────────
<Suspense fallback={<div>loading...</div>}>
{非同期コンポーネント}
</Suspense>
────────────────


■P305

app/server-functions/page.js
────────────────
const ServerFunctions = () => {
  const handleClick = () => {
    console.log("クリックされました");
  };
  return (
    <button type="button" onClick={handleClick}>
      click!!
    </button>
  );
};
export default ServerFunctions;
────────────────


■P306

app/server-functions/page.js
────────────────
const ServerFunctions = () => {
  const handleClick = async () => {
    "use server";
    console.log('クリックされました');
  };
  return (
    <button type="button" onClick={handleClick}>
      click!!
    </button>
  );
};
export default ServerFunctions;
────────────────


■P307

app/server-functions/actions.js
────────────────
"use server";

export const handleClick = async () => {
  console.log("クリックされました");
};
────────────────

app/server-functions/page.js
────────────────
"use client";
import { handleClick } from "./actions";

const ServerFunctions = () => {
  return (
    <button type="button" onClick={handleClick}>
      click!!
    </button>
  );
};
export default ServerFunctions;
────────────────


■P308

app/server-functions/actions.js
────────────────
"use server";

let count = 0;
export const incrementCount = async () => {
  count++;
  return count;
};
────────────────


■P309

app/server-functions/page.js
────────────────
"use client";
import { useState } from "react";
import { incrementCount } from "./actions";

const ServerFunctions = () => {
  const [count, setCount] = useState(0);

  const handleClick = async () => {
    const newCount = await incrementCount();
    setCount(newCount);
  };

  return (
    <div>
      <button type="button" onClick={handleClick}>
        click!!
      </button>
      <p>Count: {count}</p>
    </div>
  );
};
export default ServerFunctions;
────────────────


■P310

────────────────
const [isPending, startTransition] = useTransition();
────────────────

app/server-functions/actions.js
────────────────
"use server";

let count = 0;
export const incrementCount = async () => {
  await new Promise((resolve) => setTimeout(resolve, 2000));
  count++;
  return count;
};
────────────────

app/server-functions/page.js
────────────────
"use client";
import { useState, useTransition } from "react";
import { incrementCount } from "./actions";

const ServerFunctions = () => {
  const [count, setCount] = useState(0);
  const [isPending, startTransition] = useTransition();

  const handleClick = async () => {
    startTransition(async () => {
      const newCount = await incrementCount();
      setCount(newCount);
    });
  };

  return (
    <div>
      <button type="button" onClick={handleClick} disabled={isPending}>
        click!!
      </button>
      {isPending ? <p>incrementing...</p> : <p>Count: {count}</p>}
    </div>
  );
};
export default ServerFunctions;
────────────────


■P312

app/server-functions/page.js
────────────────
"use client";

import { useState, useActionState } from "react";
import { checkText } from "./actions";

export default function ServerFunctions() {
  const [text, setText] = useState("");
  const [state, submitAction, isPending] = useActionState(checkText, {
    default: "テキストを入力してください",
  });

  return (
    <form action={submitAction}>
      テキスト :
      <input
        type="text"
        name="myText"
        value={text}
        onChange={(e) => setText(e.target.value)}
      />
      <button type="submit" disabled={isPending}>
        submit!
      </button>
      {state.default && <p>{state.default}</p>}
      {state.error && <p style={{ color: "red" }}>{state.error}</p>}
      {state.success && <p style={{ color: "green" }}>{state.success}</p>}
    </form>
  );
}
────────────────


■P313

app/server-functions/actions.js
────────────────
"use server";

export const checkText = async (prevState, formData) => {
  const text = formData.get("myText");
  if (!text) {
    return {
      error: "必須項目です。テキストを入力してください",
    };
  }
  if (text.length > 10) {
    return {
      error: "テキストは10文字以下で入力してください",
    };
  }
  return {
    success: `入力されたテキスト「${text}」は正しいです`,
  };
};
────────────────

