Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

미션, 디스커션 제출 로직 리팩터링 (issue #765) #766

Merged
merged 9 commits into from
Nov 16, 2024
93 changes: 12 additions & 81 deletions frontend/src/components/DiscussionSubmit/index.tsx
Original file line number Diff line number Diff line change
@@ -1,98 +1,29 @@
import { useSubmitDiscussion } from '@/hooks/useSubmitDiscussion';
import DiscussionTitle from './DiscussionTitle';
import DiscussionDescription from './DiscussionDescription';
import SubmitButton from '../MissionSubmit/SubmitButton';
import useHashTags from '@/hooks/useHashTags';
import { useEffect, useState } from 'react';
import useMissions from '@/hooks/useMissions';
import TagMultipleList from '../common/TagMultipleList';
import type { HashTag } from '@/types';
import TagList from '@/components/common/TagList';
import * as S from './DiscussionSubmit.styled';
import { useSearchParams } from 'react-router-dom';
import useDiscussion from '@/hooks/useDiscussion';
import useUserInfo from '@/hooks/useUserInfo';
import { useUpdateDiscussion } from '@/hooks/useUpdateDiscussion';
import { ERROR_MESSAGE } from '@/constants/messages';
import { useSubmitDiscussionHandlers } from '@/hooks/useSubmitDiscussionHandlers';

export default function DiscussionSubmit() {
const [searchParams] = useSearchParams();
const discussionId = Number(searchParams.get('discussionId')) ?? null;
const isEditMode = !!discussionId;

const { data: discussion } = useDiscussion(discussionId);
const { data: allHashTags } = useHashTags();
const [selectedHashTags, setSelectedHashTags] = useState<HashTag[]>([]);
const [selectedMission, setSelectedMission] = useState<{ id: number; title: string } | null>(
null,
);

const { missions } = useMissions();

const { data: userInfo } = useUserInfo();
const { discussionPatchMutation } = useUpdateDiscussion(discussionId);

const hashTagIds = selectedHashTags.map((tag) => tag.id);
const useSubmitDiscussionData = {
hashTagIds,
...(selectedMission?.id && { missionId: selectedMission?.id }),
};

const {
description,
missions,
allHashTags,
selectedHashTags,
setSelectedHashTags,
selectedMission,
setSelectedMission,
discussionTitle,
isDiscussionTitleError,
isValidDiscussionTitle,
// isValidDescription,
description,
handleDiscussionTitle,
handleMarkDownDescription,
isDiscussionTitleError,
isValidDiscussionTitle,
isDescriptionError,
handleDescription,
handleSubmitDiscussion,
} = useSubmitDiscussion(useSubmitDiscussionData);

const {
title: inputTitle,
content: inputContent,
mission: inputMission,
hashTags: inputHashTags,
member,
} = discussion;

useEffect(() => {
if (isEditMode && member.id === userInfo?.id) {
if (inputTitle)
handleDiscussionTitle({
target: { value: inputTitle },
} as React.ChangeEvent<HTMLInputElement>);
if (inputContent)
handleDescription({
target: { value: inputContent },
} as React.ChangeEvent<HTMLTextAreaElement>);
if (inputMission)
setSelectedMission({
id: inputMission.id,
title: inputMission.title,
});
if (inputHashTags) setSelectedHashTags(inputHashTags);
}
}, [isEditMode, inputTitle, inputContent, inputMission, inputHashTags, member.id, userInfo?.id]);

const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

if (isEditMode && discussionId) {
discussionPatchMutation({
discussionId,
title: discussionTitle,
content: description,
missionId: selectedMission?.id,
hashTagIds: selectedHashTags.map((hashTag) => hashTag.id),
});
} else {
handleSubmitDiscussion(e);
}
};
handleFormSubmit,
} = useSubmitDiscussionHandlers();

return (
<S.DiscussionSubmitContainer>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/hooks/useDiscussion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { discussionKeys } from './queries/keys';
import type { DiscussionDetail } from '@/types/discussion';
import { getDiscussionById } from '@/apis/discussionAPI';

const useDiscussion = (discussionId?: number) => {
const useDiscussion = (discussionId: number) => {
const { data } = useQuery<DiscussionDetail>({
queryKey: discussionKeys.detail(discussionId || 0),
queryFn: discussionId ? () => getDiscussionById(discussionId) : undefined,
Expand Down
39 changes: 39 additions & 0 deletions frontend/src/hooks/useFormSubmission.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useCallback } from 'react';

interface FormSubmissionParams<T> {
isEditMode: boolean;
id: number;
handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
patchMutation: (props: T) => void;
props: T;
}

export const useFormSubmission = <T>({
isEditMode,
id,
handleSubmit,
patchMutation,
props,
}: FormSubmissionParams<T>) => {
const handleEditSubmit = useCallback(() => {
patchMutation({
...props,
id,
});
}, [patchMutation, props, id]);

const handleFormSubmit = useCallback(
(e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

if (isEditMode && id) {
handleEditSubmit();
} else {
handleSubmit(e);
}
},
[isEditMode, id, handleEditSubmit, handleSubmit],
);

return handleFormSubmit;
};
43 changes: 43 additions & 0 deletions frontend/src/hooks/useInitializeInputs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { Member } from '@/types/solution';
import type { UserInfo } from '@/types/user';
import { useCallback, useEffect } from 'react';

interface InitializeInputsParams {
isEditMode: boolean;
userInfo: UserInfo | undefined;
member: Member;
inputTitle: string;
inputDescription: string;
inputUrl: string;
handleTitle: (e: React.ChangeEvent<HTMLInputElement>) => void;
handleDescription: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
handleUrl: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

export const useInitializeInputs = ({
isEditMode,
userInfo,
member,
inputTitle,
inputDescription,
inputUrl,
handleTitle,
handleDescription,
handleUrl,
}: InitializeInputsParams) => {
const setInitialInputValues = useCallback(() => {
if (!isEditMode || member?.id !== userInfo?.id) return;

if (inputTitle)
handleTitle({ target: { value: inputTitle } } as React.ChangeEvent<HTMLInputElement>);
if (inputDescription)
handleDescription({
target: { value: inputDescription },
} as React.ChangeEvent<HTMLTextAreaElement>);
if (inputUrl) handleUrl({ target: { value: inputUrl } } as React.ChangeEvent<HTMLInputElement>);
}, [isEditMode, userInfo?.id, member?.id, inputTitle, inputDescription, inputUrl]);

Check warning on line 38 in frontend/src/hooks/useInitializeInputs.ts

View workflow job for this annotation

GitHub Actions / FE_CI

React Hook useCallback has missing dependencies: 'handleDescription', 'handleTitle', and 'handleUrl'. Either include them or remove the dependency array. If 'handleTitle' changes too often, find the parent component that defines it and wrap that definition in useCallback

useEffect(() => {
setInitialInputValues();
}, [setInitialInputValues]);
};
93 changes: 93 additions & 0 deletions frontend/src/hooks/useSubmitDiscussionHandlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { useState } from 'react';
import { useInitializeInputs } from './useInitializeInputs';
import { useFormSubmission } from './useFormSubmission';
import useDiscussion from '@/hooks/useDiscussion';
import useUserInfo from '@/hooks/useUserInfo';
import useHashTags from '@/hooks/useHashTags';
import useMissions from '@/hooks/useMissions';
import {
type DiscussionPatchMutationProps,
useUpdateDiscussion,
} from '@/hooks/useUpdateDiscussion';
import { useSubmitDiscussion } from '@/hooks/useSubmitDiscussion';
import type { HashTag } from '@/types';
import { useSearchParams } from 'react-router-dom';

export const useSubmitDiscussionHandlers = () => {
const [searchParams] = useSearchParams();
const discussionId = Number(searchParams.get('discussionId')) ?? null;

const { data: userInfo } = useUserInfo();
const { data: allHashTags } = useHashTags();
const { missions } = useMissions();

const [selectedHashTags, setSelectedHashTags] = useState<HashTag[]>([]);
const [selectedMission, setSelectedMission] = useState<{ id: number; title: string } | null>(
null,
);

const { data: discussion } = useDiscussion(discussionId);
const { discussionPatchMutation } = useUpdateDiscussion(discussionId || 0);

const {
description,
discussionTitle,
isDiscussionTitleError,
isValidDiscussionTitle,
handleDiscussionTitle,
handleMarkDownDescription,
isDescriptionError,
handleDescription,
handleSubmitDiscussion,
} = useSubmitDiscussion({
hashTagIds: selectedHashTags.map((tag) => tag.id),
missionId: selectedMission?.id,
});

const { title: inputTitle, content: inputContent, member } = discussion || {};

// 초기값 설정 로직
useInitializeInputs({
isEditMode: !!discussionId,
userInfo,
member,
inputTitle,
inputDescription: inputContent,
inputUrl: '',
handleTitle: handleDiscussionTitle,
handleDescription,
handleUrl: () => {},
});

// 폼 제출 로직
const handleFormSubmit = useFormSubmission<DiscussionPatchMutationProps>({
isEditMode: !!discussionId,
id: discussionId || 0,
handleSubmit: handleSubmitDiscussion,
patchMutation: (props: DiscussionPatchMutationProps) => discussionPatchMutation(props),
props: {
discussionId: discussionId || 0,
content: description,
hashTagIds: selectedHashTags.map((tag) => tag.id),
missionId: selectedMission?.id,
title: discussionTitle,
},
});

return {
missions,
allHashTags,
selectedHashTags,
setSelectedHashTags,
selectedMission,
setSelectedMission,
discussionTitle,
description,
handleDiscussionTitle,
handleMarkDownDescription,
isDiscussionTitleError,
isValidDiscussionTitle,
isDescriptionError,
handleFormSubmit,
};
};
3 changes: 2 additions & 1 deletion frontend/src/hooks/useSubmitDiscussionMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import useSingleRequestMutation from './useSingleRequestMutation';
import { ROUTES } from '../constants/routes';
import { useNavigate } from 'react-router-dom';
import { postDiscussionSubmit } from '@/apis/discussionAPI';
import { discussionKeys } from './queries/keys';

const SINGLE_REQUEST_ID = 'submit_discussion';

Expand All @@ -12,7 +13,7 @@ const useSubmitDiscussionMutation = () => {
const { mutate: submitDiscussionMutation, isPending } = useSingleRequestMutation({
mutationFn: postDiscussionSubmit,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['all'] }); // TODO: all, 필터링까지 캐시 무효화 잘 되는지 확인 필요 @프룬
queryClient.invalidateQueries({ queryKey: discussionKeys.all });
navigate(ROUTES.discussions);
},
onError: (error: Error) => {
Expand Down
5 changes: 0 additions & 5 deletions frontend/src/hooks/useSubmitSolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import useSubmitSolutionMutation from './useSubmitSolutionMutation';
import useModal from './useModal';
import type { FormEvent } from 'react';
import useSolutionTitle from './useSolutionTitle';
// import extractMissionName from '@/utils/extractMissionName';

interface UseSubmitSolutionParams {
missionId: number;
Expand All @@ -14,9 +13,6 @@ interface UseSubmitSolutionParams {
const useSubmitSolution = ({ missionId }: UseSubmitSolutionParams) => {
const { url, handleUrl, isValidUrl, isUrlError, setIsUrlError } = useUrl();

//TODO 임시 주석 처리
// const isMatchedMissionName = missionName === extractMissionName(url);

const {
description,
handleDescription,
Expand Down Expand Up @@ -77,7 +73,6 @@ const useSubmitSolution = ({ missionId }: UseSubmitSolutionParams) => {
isDescriptionError,
isSolutionTitleError,
isSubmitSolutionError,
// isMatchedMissionName,
};
};

Expand Down
Loading
Loading