やかんです。
「新機能!」ってほどでもないんですが、ささやかながらブログに新しい機能をつけました。
ほんっとささやか。
今まで記事一覧はリスト表示のみでした。これ↓
で、今回追加したのがこちら↓
動画でお示しするとこんな感じ↓
使いやすくなっていたら嬉しいな!といったところです。
「とりあえず機能だけ実装した」感じなのでコードはめちゃ汚いですが、「nextjsのserver componentを使いつつ、どうやって投稿データを蓄積していくの?」という場合の参考になればということでコード貼ります。
import { PostGrid } from "@/components/post-grid";
import { PostList } from "@/components/post-list";
import { getPosts } from "@/lib/get-posts";
import type { Post } from "@/lib/type";
type Props = {
page: number;
layout: "list" | "grid";
};
export async function Posts({ page, layout }: Props) {
const posts: Post[] = await getPosts({ page });
return layout === "list" ? (
<PostList posts={posts} page={page} listPagePath={() => `/posts`} />
) : (
<PostGrid posts={posts} page={page} />
);
}
"use client";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { BsChevronDoubleDown } from "react-icons/bs";
import { PostGridCard } from "@/components/post-grid-card";
import { buttonVariants } from "@/components/ui/button";
import { BeatLoader } from "@/components/ui/loader";
import { WidthWrapper } from "@/components/width-wrapper";
import type { Post } from "@/lib/type";
import { cn } from "@/lib/utils";
type Props = {
posts: Post[];
page: number;
};
export function PostGrid({ posts: initialPosts, page }: Props) {
const [posts, setPosts] = useState<Post[]>();
const [loading, setLoading] = useState<boolean>(false);
const router = useRouter();
useEffect(() => {
const newPosts = posts ? [...posts, ...initialPosts] : initialPosts;
// idが同じ投稿を削除
const uniquePosts = newPosts.filter(
(post, index, self) => index === self.findIndex((p) => p.id === post.id),
);
// 1ページ目じゃないのに10件しかないということは、それ以前のページのデータが取得できていないということ
if (page !== 1 && uniquePosts.length === 10) {
router.push("/posts?layout=grid&page=1");
return;
}
setPosts(uniquePosts);
setLoading(false);
// 一番下にスクロール
if (page !== 1) {
window.scrollTo(0, document.body.scrollHeight);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [initialPosts]);
return posts ? (
<WidthWrapper>
<div className="grid grid-cols-3 gap-8">
{posts?.map((post) => <PostGridCard key={post.id} post={post} />)}
</div>
<Link
href={`?layout=grid&page=${page + 1}`}
onClick={() => setLoading(true)}
className={cn(
buttonVariants({ variant: "outline" }),
"my-8 flex w-full items-center justify-center space-x-2 font-bold",
)}
>
{loading ? (
<BeatLoader color="#475569" />
) : (
<>
<span>さらに表示</span>
<BsChevronDoubleDown className="font-bold text-black" />
</>
)}
</Link>
</WidthWrapper>
) : (
<div className="mt-[20vh] flex min-h-screen justify-center">
<BeatLoader color="#475569" />
</div>
);
}
うーん、もっといいやり方絶対あるよな。ハリボテすぎる。まあ、追ってリファクタしていきます!
ということで、こちらの記事は以上です。最後までお読みいただき、ありがとうございます。