Skip to content

Latest commit

 

History

History
142 lines (114 loc) · 5.71 KB

05.md

File metadata and controls

142 lines (114 loc) · 5.71 KB

5. 「好き」ボタンを追加しよう

商品に「好き」ボタンを追加してみましょう。押された回数が表示できるとよいですね。

やること

src/app/items/[id]/like.tsx に、下記のファイルを作成します。これが「好き」ボタンです。

"use client";

import { useState } from "react";

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

  return (
    <div className="flex items-center gap-2">
      <button
        className="bg-blue-500 text-white px-4 py-2 rounded-md"
        onClick={() => setCount(count + 1)}
      >
        好き
      </button>
      <span className="text-gray-600">{count}</span>
    </div>
  );
};

export default Like;

そしてこの「好き」ボタンを商品詳細ページ(app/items/[id]/page.tsx)に追加します。

import items from "../../../data/items.json";
import Like from "./like";

type Props = {
  params: {
    id: string;
  };
};

export default async function ItemDetail(props: Props) {
  const item = items.find((item) => item.id === parseInt(props.params.id));

  if (!item) {
    return (
      <main>
        <h1>商品が見つかりませんでした</h1>
      </main>
    );
  }

  return (
    <main className="container mx-auto px-4 py-8">
      <a href="/" className="inline-block m-4 text-blue-600 hover:text-blue-800">
        ← 商品一覧に戻る
      </a>
      <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
        <div>
          <img src={item.image} alt={item.name} className="w-full rounded-lg shadow-lg" />
        </div>
        <div>
          <h1 className="text-3xl font-bold m-1">{item.name}</h1>
          <p className="text-gray-600 m-8">{item.description}</p>
          <div className="bg-blue-100 m-4 p-6 rounded-lg ">
            <p className="text-3xl font-bold text-blue-600">{item.price.toLocaleString()}</p>
            <p className="text-gray-600 m-2">カテゴリー: {item.category}</p>
          </div>
          <Like /> {/* 追加 */}
        </div>
      </div>
    </main>
  );
}

これで「好き」ボタンを押すとカウントが増えるようになりました!

解説

use client は、このファイルがブラウザで実行されることを示します。いままではuse clientをつけていなかったので、サーバーで実行されていました。ブラウザで実行するようにすると、「ボタンを押した」などのユーザーの操作を感知できるようになります。ブラウザで実行するのとサーバーで実行するのでは様々な違いがあるのですが、ここではuse clientをつけるとユーザーの操作を感知できるようになるということを覚えておけばOKです。

useState は、Reactの状態管理をするための関数です。Reactのコンポーネントはなにか変化があると再度画面がレンダリングされます。useState は、その変化する値を作り出すための関数です。二つの値、countsetCountを返します。countは現在の値で、setCountは値を更新するための関数です。

onClickに関数を渡すと、ボタンがクリックされたときにその関数が実行されます。ここではボタンが押されたら「好き」のカウントを増やしたいので、setCountを呼び出してcountの値を更新しています。

やること

10回目の「好き」を祝いましょう!下記のコンポーネントは表示されるとめでたい画面になるように作られています。src/app/items/[id]/like.tsx に下記のコンポーネントを追加して、10回目の「好き」で表示されるように実装を変更してみましょう。出したり消したりするのは、count を条件に使って、このCelebrationコンポーネントの表示・非表示を切りかえればよさそうです。

const Celebration = () => {
  const emojiCount = 50;
  const fontSize = 100;
  return (
    <div className="fixed inset-0 pointer-events-none">
      <>
        <style jsx global>{`
          @keyframes fall {
            0% {
              transform: translateY(-100vh) rotate(0deg);
              opacity: 1;
            }
            100% {
              transform: translateY(100vh) rotate(360deg);
              opacity: 0;
            }
          }
        `}</style>
        {Array.from({ length: emojiCount }).map((_, i) => (
          <div
            key={i}
            className="absolute"
            style={{
              left: `${Math.random() * 100}%`,
              top: `${Math.random() * 100}%`,
              animation: `fall ${2 + Math.random() * 3}s linear forwards`,
              fontSize: `${100 + fontSize * Math.random()}px`,
            }}
          >
            ☺️
          </div>
        ))}
      </>
    </div>
  );
};

ヒント1: JavaScriptでは % で余りを計算できます。9 % 109で、10 % 10011 % 101です。 ヒント2: { showCelebration ? <Celebration /> : null } とすると、条件(ここではshowCelebration)によって表示する内容を切り替えることができます。

解説

CelebrationではCSSのアニメーションを使っています。詳細な説明は省略しますが、@keyframesfallというアニメーションを定義し、animation でアニメーションを適用しています。

emojiCountfontSizeを変更すると、表示される絵文字の数や大きさを変更できます。またを変更すれば出力される絵文字も変更できます。️お好きな量・お好きな大きさで、お好きな絵文字をたくさん出してみてください。