Skip to content

Commit

Permalink
fix: on new comment input re-renders only comment form
Browse files Browse the repository at this point in the history
  • Loading branch information
DenisVorop committed Oct 9, 2023
1 parent 1a91635 commit b24bfae
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 54 deletions.
14 changes: 3 additions & 11 deletions src/components/GoalActivityFeed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { AddCriteriaForm } from './CriteriaForm/CriteriaForm';

const ModalOnEvent = dynamic(() => import('./ModalOnEvent'));
const GoalEditForm = dynamic(() => import('./GoalEditForm/GoalEditForm'));
const CommentCreateForm = dynamic(() => import('./CommentCreateForm/CommentCreateForm'));
const GoalCommentCreateForm = dynamic(() => import('./GoalCommentCreateForm'));

interface GoalActivityFeedProps {
goal: NonNullable<GoalByIdReturnType>;
Expand All @@ -40,10 +40,7 @@ export const GoalActivityFeed = forwardRef<HTMLDivElement, GoalActivityFeedProps
onGoalCriteriaUpdate,
onGoalCriteriaRemove,
onGoalCriteriaConvert,
resolveGoalCommentDraft,
onGoalCommentChange,
onGoalCommentCreate,
onGoalCommentCancel,
onGoalCommentReactionToggle,
onGoalCommentDelete,
onGoalDependencyAdd,
Expand All @@ -64,8 +61,6 @@ export const GoalActivityFeed = forwardRef<HTMLDivElement, GoalActivityFeedProps
},
);

const commentDraft = resolveGoalCommentDraft(goal?.id);

const onConfirmDeletingGoal = useCallback(() => {
onGoalDelete();
onGoalDeleteConfirm?.();
Expand Down Expand Up @@ -148,13 +143,10 @@ export const GoalActivityFeed = forwardRef<HTMLDivElement, GoalActivityFeedProps
</>
}
footer={
<CommentCreateForm
<GoalCommentCreateForm
goalId={goal.id}
states={goal._isEditable ? goal.project?.flow.states : undefined}
stateId={commentDraft?.stateId}
description={commentDraft?.description}
onSubmit={onGoalCommentCreate}
onCancel={onGoalCommentCancel}
onChange={onGoalCommentChange}
/>
}
renderCommentItem={(value) => (
Expand Down
61 changes: 61 additions & 0 deletions src/components/GoalCommentCreateForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ComponentProps, FC, useCallback } from 'react';
import type { Comment } from '@prisma/client';

import { useLSDraft } from '../hooks/useLSDraft';

import CommentCreateForm from './CommentCreateForm/CommentCreateForm';

interface GoalCommentCreateFormProps {
states: ComponentProps<typeof CommentCreateForm>['states'];
goalId: string;
onSubmit: (comment?: { description: string; stateId?: string }) => Promise<Comment | null | undefined>;
}

const GoalCommentCreateForm: FC<GoalCommentCreateFormProps> = ({ states, goalId, onSubmit }) => {
const {
saveDraft: saveGoalCommentDraft,
resolveDraft: resolveGoalCommentDraft,
removeDraft: removeGoalCommentDraft,
} = useLSDraft('draftGoalComment', {});

const onGoalCommentChange = useCallback(
(comment?: { stateId?: string; description?: string }) => {
if (!comment?.description) {
removeGoalCommentDraft(goalId);
return;
}

saveGoalCommentDraft(goalId, comment);
},
[goalId, removeGoalCommentDraft, saveGoalCommentDraft],
);

const onGoalCommentCancel = useCallback(() => {
removeGoalCommentDraft(goalId);
}, [goalId, removeGoalCommentDraft]);

const commentDraft = resolveGoalCommentDraft(goalId);

const onGoalCommentSubmit = useCallback(
async (comment?: { description: string; stateId?: string }) => {
const data = await onSubmit(comment);
if (data) {
removeGoalCommentDraft(goalId);
}
},
[goalId, onSubmit, removeGoalCommentDraft],
);

return (
<CommentCreateForm
states={states}
stateId={commentDraft?.stateId}
description={commentDraft?.description}
onSubmit={onGoalCommentSubmit}
onCancel={onGoalCommentCancel}
onChange={onGoalCommentChange}
/>
);
};

export default GoalCommentCreateForm;
7 changes: 3 additions & 4 deletions src/components/ProjectsPage/ProjectsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MouseEventHandler, useCallback, useEffect, useMemo, useRef } from 'react';
import { MouseEventHandler, useCallback, useEffect, useMemo } from 'react';
import { useRouter as useNextRouter } from 'next/router';
import dynamic from 'next/dynamic';
import NextLink from 'next/link';
Expand Down Expand Up @@ -41,7 +41,6 @@ export const projectsSize = 20;
export const ProjectsPage = ({ user, ssrTime, params: { id }, defaultPresetFallback }: ExternalPageProps) => {
const nextRouter = useNextRouter();
const [, setCurrentProjectCache] = useLocalStorage('currentProjectCache', null);
const setCurrentProjectCacheRef = useRef(setCurrentProjectCache);
const { toggleFilterStar } = useFilterResource();

const utils = trpc.useContext();
Expand Down Expand Up @@ -137,13 +136,13 @@ export const ProjectsPage = ({ user, ssrTime, params: { id }, defaultPresetFallb
useEffect(() => {
if (!project) return;

setCurrentProjectCacheRef.current({
setCurrentProjectCache({
id: project.id,
title: project.title,
description: project.description ?? undefined,
flowId: project.flowId,
});
}, [project]);
}, [project, setCurrentProjectCache]);

useWillUnmount(() => {
setCurrentProjectCache(null);
Expand Down
34 changes: 2 additions & 32 deletions src/hooks/useGoalResource.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useMemo } from 'react';
import { Activity, GoalAchieveCriteria } from '@prisma/client';
import type { Activity, GoalAchieveCriteria } from '@prisma/client';

import { trpc } from '../utils/trpcClient';
import { notifyPromise } from '../utils/notifyPromise';
Expand All @@ -13,7 +13,6 @@ import {
} from '../schema/criteria';
import { ModalEvent, dispatchModalEvent } from '../utils/dispatchModal';

import { useLSDraft } from './useLSDraft';
import { useHighlightedComment } from './useHighlightedComment';
import { useReactionsResource } from './useReactionsResource';

Expand Down Expand Up @@ -87,11 +86,6 @@ export const useGoalResource = (fields: GoalFields, config?: InvalidateConfigura
const updateGoalOwnerMutation = trpc.goal.updateOwner.useMutation();
const createGoalMutation = trpc.goal.create.useMutation();

const {
saveDraft: saveGoalCommentDraft,
resolveDraft: resolveGoalCommentDraft,
removeDraft: removeGoalCommentDraft,
} = useLSDraft('draftGoalComment', {});
const { highlightCommentId, setHighlightCommentId } = useHighlightedComment();
const { commentReaction } = useReactionsResource(fields.reactions);

Expand Down Expand Up @@ -178,20 +172,6 @@ export const useGoalResource = (fields: GoalFields, config?: InvalidateConfigura
[removeGoalDependency, invalidate],
);

const onGoalCommentChange = useCallback(
(comment?: { stateId?: string; description?: string }) => {
if (!id) return;

if (!comment?.description) {
removeGoalCommentDraft(id);
return;
}

saveGoalCommentDraft(id, comment);
},
[id, removeGoalCommentDraft, saveGoalCommentDraft],
);

const onGoalCommentCreate = useCallback(
async (comment?: { description: string; stateId?: string }, invalidateKey?: RefetchKeys | RefetchKeys[]) => {
if (!comment || !id) return;
Expand All @@ -204,15 +184,14 @@ export const useGoalResource = (fields: GoalFields, config?: InvalidateConfigura
const [data] = await notifyPromise(promise, 'commentCreate');

if (data) {
removeGoalCommentDraft(data.id);
setHighlightCommentId(data.id);

invalidate(invalidateKey);
}

return data;
},
[createGoalComment, id, invalidate, removeGoalCommentDraft, setHighlightCommentId],
[createGoalComment, id, invalidate, setHighlightCommentId],
);

const onGoalCommentUpdate = useCallback(
Expand All @@ -234,12 +213,6 @@ export const useGoalResource = (fields: GoalFields, config?: InvalidateConfigura
[invalidate, updateGoalComment],
);

const onGoalCommentCancel = useCallback(() => {
if (id) {
removeGoalCommentDraft(id);
}
}, [id, removeGoalCommentDraft]);

const onGoalCommentReactionToggle = useCallback(
(id: string, invalidateKey?: RefetchKeys | RefetchKeys[]) => {
return commentReaction(id, () => invalidate(invalidateKey));
Expand Down Expand Up @@ -433,7 +406,6 @@ export const useGoalResource = (fields: GoalFields, config?: InvalidateConfigura
lastStateComment,

invalidate,
resolveGoalCommentDraft,

goalCreate,
goalUpdate,
Expand All @@ -459,10 +431,8 @@ export const useGoalResource = (fields: GoalFields, config?: InvalidateConfigura
onGoalCriteriaRemove,
onGoalCriteriaConvert,

onGoalCommentChange,
onGoalCommentCreate,
onGoalCommentUpdate,
onGoalCommentCancel,
onGoalCommentReactionToggle,
onGoalCommentDelete,
};
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useLSDraft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type DraftComment = DraftGoalComment[keyof DraftGoalComment];
type DraftGoalCommentKey = 'draftGoalComment';

function useLSDraft<KDG extends DraftGoalCommentKey>(storageKey: KDG, initialValue: Record<string, LSParams[1]>) {
const [draft, setDraft] = useLocalStorage(storageKey, initialValue);
const [, setDraft, draftRef] = useLocalStorage(storageKey, initialValue);

const saveDraft = useCallback(
(id: string, draft: DraftComment) => {
Expand All @@ -20,7 +20,7 @@ function useLSDraft<KDG extends DraftGoalCommentKey>(storageKey: KDG, initialVal
[setDraft],
);

const resolveDraft = useCallback((id?: string) => (id ? draft[id] : undefined), [draft]);
const resolveDraft = useCallback((id?: string) => (id ? draftRef.current[id] : undefined), [draftRef]);

const removeDraft = useCallback(
(id: string) => {
Expand Down
11 changes: 11 additions & 0 deletions src/hooks/useLatest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useLayoutEffect, useRef } from 'react';

export function useLatest<T>(value: T) {
const ref = useRef(value);

useLayoutEffect(() => {
ref.current = value;
}, [value]);

return ref;
}
13 changes: 8 additions & 5 deletions src/hooks/useLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useCallback, useState } from 'react';
import { MutableRefObject, useCallback, useState } from 'react';

import { safelyParseJson } from '../utils/safelyParseJson';

import { useLatest } from './useLatest';

export type LastOrCurrentProject = { id: string; title: string; flowId: string; description?: string | null } | null;
export type RecentProjectsCache = Record<
string,
Expand All @@ -22,7 +24,7 @@ export function useLocalStorage<T extends keyof StorageKey>(
storageKey: T,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
defaultValue?: any,
): [StorageKey[T], SetValue<StorageKey[T]>] {
): [StorageKey[T], SetValue<StorageKey[T]>, MutableRefObject<StorageKey[T]>] {
const safelySetStorage = useCallback(
(valueToStore: string) => {
try {
Expand All @@ -45,14 +47,15 @@ export function useLocalStorage<T extends keyof StorageKey>(
return safelyParseJson(valueToStore);
});

const storedValueRef = useLatest(storedValue);
const setValue: SetValue<StorageKey[T]> = useCallback(
(value) => {
const valueToStore = value instanceof Function ? value(storedValue) : value;
const valueToStore = value instanceof Function ? value(storedValueRef.current) : value;
safelySetStorage(JSON.stringify(valueToStore));
setStoredValue(valueToStore);
},
[safelySetStorage, storedValue],
[safelySetStorage, storedValueRef],
);

return [storedValue, setValue];
return [storedValue, setValue, storedValueRef];
}

0 comments on commit b24bfae

Please sign in to comment.