Skip to content

Commit

Permalink
React: Optimize performance
Browse files Browse the repository at this point in the history
  • Loading branch information
fuma-nama committed Jul 24, 2024
1 parent 63ca085 commit 811c1dc
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 37 deletions.
5 changes: 5 additions & 0 deletions .changeset/moody-mirrors-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fuma-comment/react": patch
---

Optimize performance
66 changes: 39 additions & 27 deletions packages/react/src/components/comment/actions.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { ThumbsDown, ThumbsUp } from "lucide-react";
import { cva } from "class-variance-authority";
import { useCallback } from "react";
import { useCallback, useRef } from "react";
import type { SerializedComment } from "@fuma-comment/server";
import { useCommentContext } from "../../contexts/comment";
import { useAuthContext } from "../../contexts/auth";
import { useLatestCallback } from "../../utils/hooks";
import { deleteRate, setRate } from "../../utils/fetcher";
import { onLikeUpdated } from "../../utils/comment-manager";
import { cn } from "../../utils/cn";
import { Dialog, DialogContent, DialogTrigger } from "../dialog";
import { buttonVariants } from "../button";
import type { UseCommentEditor } from "../editor";
import { ReplyForm, ReplyHeader } from "./reply-form";

const rateVariants = cva(
Expand All @@ -33,26 +34,24 @@ export function Actions({
canReply?: boolean;
}): React.ReactNode {
const { comment, isReplying, setReply } = useCommentContext();
const editorRef = useRef<UseCommentEditor>();
const { session } = useAuthContext();
const isAuthenticated = session !== null;

const onRate = useLatestCallback((v: boolean) => {
// double click
if (v === comment.liked) {
void deleteRate({
id: comment.id,
page: comment.page,
});
onLikeUpdated(comment.id, undefined);
} else {
void setRate({
id: comment.id,
page: comment.page,
like: v,
});
onLikeUpdated(comment.id, v);
}
});
const onLike = useCallback(() => {
setLike(comment, true);
}, [comment]);

const onDislike = useCallback(() => {
setLike(comment, false);
}, [comment]);

const onOpenAutoFocus = useCallback((e: Event) => {
setTimeout(() => {
editorRef.current?.commands.focus();
}, 10);
e.preventDefault();
}, []);

return (
<div className="mt-2 flex flex-row gap-1">
Expand All @@ -63,9 +62,7 @@ export function Actions({
}),
)}
disabled={!isAuthenticated}
onClick={useCallback(() => {
onRate(true);
}, [onRate])}
onClick={onLike}
type="button"
>
<ThumbsUp aria-label="Like" className="size-4" />
Expand All @@ -78,9 +75,7 @@ export function Actions({
}),
)}
disabled={!isAuthenticated}
onClick={useCallback(() => {
onRate(false);
}, [onRate])}
onClick={onDislike}
type="button"
>
<ThumbsDown aria-label="Dislike" className="size-4" />
Expand All @@ -91,12 +86,29 @@ export function Actions({
<DialogTrigger className={cn(rateVariants({ active: false }))}>
Reply
</DialogTrigger>
<DialogContent>
<DialogContent onOpenAutoFocus={onOpenAutoFocus}>
<ReplyHeader />
<ReplyForm />
<ReplyForm editorRef={editorRef} />
</DialogContent>
</Dialog>
) : null}
</div>
);
}

function setLike(comment: SerializedComment, v: boolean): void {
if (v === comment.liked) {
void deleteRate({
id: comment.id,
page: comment.page,
});
onLikeUpdated(comment.id, undefined);
} else {
void setRate({
id: comment.id,
page: comment.page,
like: v,
});
onLikeUpdated(comment.id, v);
}
}
21 changes: 13 additions & 8 deletions packages/react/src/components/comment/reply-form.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import useSWRMutation from "swr/mutation";
import { type ReactNode, useCallback, useRef, useState } from "react";
import {
type MutableRefObject,
type ReactNode,
useCallback,
useState,
} from "react";
import { cn } from "../../utils/cn";
import {
type FetcherError,
Expand Down Expand Up @@ -39,10 +44,13 @@ export function ReplyHeader(): ReactNode {
);
}

export function ReplyForm(): ReactNode {
export function ReplyForm({
editorRef,
}: {
editorRef: MutableRefObject<UseCommentEditor | undefined>;
}): ReactNode {
const { page } = useCommentsContext();
const [isEmpty, setIsEmpty] = useState(true);
const editorRef = useRef<UseCommentEditor>();
const { comment, setReply } = useCommentContext();

const mutation = useSWRMutation(
Expand All @@ -68,8 +76,6 @@ export function ReplyForm(): ReactNode {
setReply(false);
});

const disabled = mutation.isMutating;

const submit = useLatestCallback(() => {
if (!editorRef.current) return;
const content = editorRef.current.getJSON();
Expand All @@ -86,8 +92,7 @@ export function ReplyForm(): ReactNode {
return (
<form onSubmit={onSubmit}>
<CommentEditor
autofocus
disabled={disabled}
disabled={mutation.isMutating}
editorRef={editorRef}
onChange={useCallback((v: UseCommentEditor) => {
setIsEmpty(v.isEmpty);
Expand All @@ -99,7 +104,7 @@ export function ReplyForm(): ReactNode {
<div className="mt-2 flex flex-row gap-1">
<button
className={cn(buttonVariants({ className: "gap-2" }))}
disabled={disabled || isEmpty}
disabled={mutation.isMutating || isEmpty}
type="submit"
>
{mutation.isMutating ? <Spinner /> : null}
Expand Down
3 changes: 1 addition & 2 deletions packages/react/src/components/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import { createEditorLazy } from "./lazy-load";
export type UseCommentEditor = Editor;

export interface EditorProps {
autofocus?: boolean;
defaultValue?: JSONContent;
placeholder?: string;
disabled?: boolean;
Expand Down Expand Up @@ -82,7 +81,7 @@ export const CommentEditor = forwardRef<HTMLDivElement, EditorProps>(
let instance: Editor | undefined;

void createEditorLazy({
autofocus: initialProps.current.autofocus,
autofocus: false,
content: initialProps.current.defaultValue,
editorProps: {
attributes: {
Expand Down

0 comments on commit 811c1dc

Please sign in to comment.