perf: fetch session and comments in parallel, pass current user to comment components instead of useSession

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
jakeandClaude Sonnet 5.5 committed 2026-10-03 08:48:15 -04:00
1 parent 1d8b8358ca
commit 6527c7081c
6 files changed
+56 -36

No files matched your search

+11 -7
View File
@@ -22,8 +22,7 @@ import {
} from "@/components/ui/dropdown-menu";
import { Spinner } from "@/components/ui/spinner";
import { toast } from "@/components/ui/toast";
import { useSession } from "@/lib/auth-client";
import type { CommentWithUser } from "@/lib/data/comments";
import type { CommentUser, CommentWithUser } from "@/lib/data/comments";
import { deleteComment } from "@/lib/server/comments";
import { EditCommentForm, ReplyForm } from "./comment-form";
@@ -35,12 +34,16 @@ type ActionMode =
| { type: "confirming-delete" }
| { type: "deleting" };
const CommentActions = ({ comment }: { comment: CommentWithUser }) => {
const CommentActions = ({
comment,
currentUser,
}: {
comment: CommentWithUser;
currentUser: CommentUser | null;
}) => {
const [mode, setMode] = useState<ActionMode>({ type: "idle" });
const { data: session } = useSession();
if (!session) return null;
if (!currentUser) return null;
const handleDelete = async () => {
setMode({ type: "deleting" });
@@ -87,7 +90,7 @@ const CommentActions = ({ comment }: { comment: CommentWithUser }) => {
Reply
</Button>
{session.user.id === comment.user.id && (
{currentUser.id === comment.user.id && (
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="outline" size="sm" />}>
<IconDots />
@@ -117,6 +120,7 @@ const CommentActions = ({ comment }: { comment: CommentWithUser }) => {
<ReplyForm
slug={comment.pageSlug}
parentId={comment.id}
currentUser={currentUser}
onCancel={() => setMode({ type: "idle" })}
onSuccess={() => setMode({ type: "idle" })}
/>
+12 -16
View File
@@ -8,7 +8,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover
import { Spinner } from "@/components/ui/spinner";
import { Textarea } from "@/components/ui/textarea";
import { toast } from "@/components/ui/toast";
import { useSession } from "@/lib/auth-client";
import type { CommentUser } from "@/lib/data/comments";
import { createComment, updateComment } from "@/lib/server/comments";
import { CommentAvatar } from "./comment-avatar";
@@ -94,18 +94,12 @@ const CommentTextarea = ({
/>
);
// Current user's avatar (uses session)
const CurrentUserAvatar = () => {
const { data: session } = useSession();
if (!session?.user) return null;
return (
<div className="shrink-0">
<CommentAvatar name={session.user.name} image={session.user.image} />
</div>
);
};
// Current user's avatar
const CurrentUserAvatar = ({ user }: { user: CommentUser }) => (
<div className="shrink-0">
<CommentAvatar name={user.name} image={user.image} />
</div>
);
// Submit button with pending state
const SubmitButton = ({
@@ -196,7 +190,7 @@ const MarkdownHelp = () => (
);
// New comment form - for creating top-level comments
const NewCommentForm = ({ slug }: { slug: string }) => {
const NewCommentForm = ({ slug, currentUser }: { slug: string; currentUser: CommentUser }) => {
const { content, setContent, isPending, startTransition } = useCommentFormState();
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
@@ -225,7 +219,7 @@ const NewCommentForm = ({ slug }: { slug: string }) => {
return (
<form onSubmit={handleSubmit} className="space-y-4" data-intent="create">
<div className="flex gap-4">
<CurrentUserAvatar />
<CurrentUserAvatar user={currentUser} />
<div className="min-w-0 flex-1 space-y-4">
<CommentTextarea
@@ -257,11 +251,13 @@ const NewCommentForm = ({ slug }: { slug: string }) => {
const ReplyForm = ({
slug,
parentId,
currentUser,
onCancel,
onSuccess,
}: {
slug: string;
parentId: string;
currentUser: CommentUser;
onCancel: () => void;
onSuccess?: () => void;
}) => {
@@ -294,7 +290,7 @@ const ReplyForm = ({
return (
<form onSubmit={handleSubmit} className="space-y-4" data-intent="create">
<div className="flex gap-4">
<CurrentUserAvatar />
<CurrentUserAvatar user={currentUser} />
<div className="min-w-0 flex-1 space-y-4">
<CommentTextarea
+9 -3
View File
@@ -2,7 +2,7 @@ import Link from "next/link";
import Markdown from "react-markdown";
import { RelativeTime } from "@/components/relative-time";
import type { CommentWithUser } from "@/lib/data/comments";
import type { CommentUser, CommentWithUser } from "@/lib/data/comments";
import { rehypeExternalLinks } from "@/lib/rehype";
import { remarkGfm, remarkSmartypants } from "@/lib/remark";
import { cn } from "@/lib/utils";
@@ -10,7 +10,13 @@ import { cn } from "@/lib/utils";
import { CommentActions } from "./comment-actions";
import { CommentAvatar } from "./comment-avatar";
const CommentSingle = ({ comment }: { comment: CommentWithUser }) => {
const CommentSingle = ({
comment,
currentUser,
}: {
comment: CommentWithUser;
currentUser: CommentUser | null;
}) => {
const divId = `comment-${comment.id.substring(0, 8)}`;
return (
@@ -62,7 +68,7 @@ const CommentSingle = ({ comment }: { comment: CommentWithUser }) => {
</Markdown>
</div>
<CommentActions comment={comment} />
<CommentActions comment={comment} currentUser={currentUser} />
</div>
</div>
</div>
+5 -2
View File
@@ -1,4 +1,4 @@
import type { CommentWithUser } from "@/lib/data/comments";
import type { CommentUser, CommentWithUser } from "@/lib/data/comments";
import { cn } from "@/lib/utils";
import { CommentSingle } from "./comment-single";
@@ -10,15 +10,17 @@ const CommentThread = ({
comment,
replies,
allComments,
currentUser,
level = 0,
}: {
comment: CommentWithUser;
replies: CommentWithUser[];
allComments: Record<string, CommentWithUser[]>;
currentUser: CommentUser | null;
level?: number;
}) => (
<>
<CommentSingle comment={comment} />
<CommentSingle comment={comment} currentUser={currentUser} />
{replies.length > 0 && (
<div className={cn("mt-6 space-y-6", level < MAX_NESTING_LEVEL && "ml-6 border-l-2 pl-6")}>
@@ -28,6 +30,7 @@ const CommentThread = ({
comment={reply}
replies={allComments[reply.id] || []}
allComments={allComments}
currentUser={currentUser}
level={level + 1}
/>
))}
+15 -7
View File
@@ -1,18 +1,25 @@
import { headers } from "next/headers";
import { auth } from "@/lib/auth";
import { type CommentWithUser, getComments } from "@/lib/data/comments";
import { type CommentUser, type CommentWithUser, getComments } from "@/lib/data/comments";
import { NewCommentForm } from "./comment-form";
import { CommentThread } from "./comment-thread";
import { SignIn } from "./sign-in";
const Comments = async ({ slug }: { slug: string }) => {
const session = await auth.api.getSession({
headers: await headers(),
});
// independent queries -- run them in parallel
const [session, comments] = await Promise.all([
auth.api.getSession({
headers: await headers(),
}),
getComments(slug),
]);
const comments = await getComments(slug);
// only pass the public fields down to client components
const currentUser: CommentUser | null = session
? { id: session.user.id, name: session.user.name, image: session.user.image ?? null }
: null;
const commentsByParentId = comments.reduce(
(acc, comment) => {
@@ -30,8 +37,8 @@ const Comments = async ({ slug }: { slug: string }) => {
return (
<>
{session ? (
<NewCommentForm slug={slug} />
{currentUser ? (
<NewCommentForm slug={slug} currentUser={currentUser} />
) : (
<div className="flex flex-col items-center justify-center gap-y-4 rounded-lg bg-muted/40 p-6">
<p className="text-center font-medium">Join the discussion by signing in:</p>
@@ -47,6 +54,7 @@ const Comments = async ({ slug }: { slug: string }) => {
comment={comment}
replies={commentsByParentId[comment.id] || []}
allComments={commentsByParentId}
currentUser={currentUser}
/>
))}
</div>
+4 -1
View File
@@ -4,8 +4,11 @@ import { desc, eq, sql } from "drizzle-orm";
import { db } from "@/lib/db";
import * as schema from "@/lib/db/schema";
/** The public profile fields of a commenter (also used for the signed-in user in comment UI) */
export type CommentUser = Pick<typeof schema.user.$inferSelect, "id" | "name" | "image">;
export type CommentWithUser = typeof schema.comment.$inferSelect & {
user: Pick<typeof schema.user.$inferSelect, "id" | "name" | "image">;
user: CommentUser;
};
/**