Skip to content

Commit

Permalink
Refactor : 파일 및 파일인풋 훅스 분리 (#68)
Browse files Browse the repository at this point in the history
* Refactor : 파일 및 파일인풋 훅스 분리

* Refactor : callback 구조 변경

* Refactor : 게시글 삭제 로직 변경 (첨부파일 함께 삭제)
  • Loading branch information
jobkaeHenry authored Dec 4, 2023
1 parent de1f5dc commit be8b4c6
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 83 deletions.
83 changes: 33 additions & 50 deletions client/src/app/(protectedRoute)/new-post/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { Box, Container, Paper, Tooltip } from "@mui/material";

import { useRouter } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
import { useCallback, useState } from "react";
import HOME from "@/const/clientPath";
import PictureIcon from "@/assets/icons/PictureIcon.svg";
import PinIcon from "@/assets/icons/PinIcon.svg";
Expand All @@ -21,6 +21,8 @@ import CustomAppbar from "@/components/CustomAppbar";
import SquareIconButton from "@/components/SquareIconButton";
import PreviewImageByURL from "@/components/PreviewImageByURL";
import NewPostTextEditor from "@/components/newpost/NewPostTextEditor";
import useRenderAsDataUrl from "@/hooks/useRenderAsDataUrl";
import SingleImageInput from "@/components/SingleImageInput";

export default function NewpostPage() {
const { setLoading } = useGlobalLoadingStore();
Expand All @@ -38,52 +40,43 @@ export default function NewpostPage() {
useState<NewPostRequestAlCohol["alcoholNo"]>();

const [file, setFile] = useState<File>();
const [fileUrl, setFileUrl] = useState<string | ArrayBuffer | null>();
const fileUrl = useRenderAsDataUrl(file);

const [isSuccess, SetIsSuccess] = useState(false);

useEffect(() => {
if (!file) {
return;
}
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => setFileUrl(reader.result);
}, [file]);

const { mutateAsync: newPostHandler } = useNewPostMutation();
const { mutateAsync: attachFileHandler } = useNewAttachMutation();
const { mutateAsync: deletePostHandler } = useDeletePostMutation();

const submitHandler = useCallback(async () => {
setLoading(true);
let postNo;
try {
const { postNo: res } = await newPostHandler({
...formValue,
alcoholNo,
});
postNo = res;
if (file) {
try {
await attachFileHandler({
file,
url: { pk: postNo, type: "POST" },
});
} catch {
deletePostHandler(postNo);
return;
const submitHandler = useCallback(
async (formValue: NewPostRequestInterface, file?: File) => {
setLoading(true);
let postNo;
try {
const { postNo: res } = await newPostHandler(formValue);
postNo = res;
if (file) {
try {
await attachFileHandler({
file,
url: { pk: postNo, type: "POST" },
});
} catch {
deletePostHandler(postNo);
return;
}
}
invalidatePreviousPost();
SetIsSuccess(true);
router.push(HOME);
} catch {
return;
} finally {
setLoading(false);
}
invalidatePreviousPost();
SetIsSuccess(true);
router.push(HOME);
} catch {
return;
} finally {
setLoading(false);
}
}, [formValue, alcoholNo, router, file]);
},
[router]
);

return (
<Paper>
Expand All @@ -92,7 +85,7 @@ export default function NewpostPage() {
title="포스팅"
appendButton="공유"
disableAppend={isSuccess}
onClickAppend={submitHandler}
onClickAppend={()=>submitHandler({...formValue,alcoholNo},file)}
/>

<Container sx={{ p: { xs: 0, sm: 4 } }} maxWidth={"lg"}>
Expand Down Expand Up @@ -127,17 +120,7 @@ export default function NewpostPage() {
component={"label"}
iconComponent={<PictureIcon />}
>
<input
name="image"
style={{ display: "none" }}
type="file"
accept="image/*"
onChange={(e) => {
if (e.target.files) {
setFile(e.target.files[0]);
}
}}
/>
<SingleImageInput onChange={(file) => setFile(file)} />
</SquareIconButton>
</Tooltip>
{/* 위치 */}
Expand Down
32 changes: 32 additions & 0 deletions client/src/components/SingleImageInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { InputHTMLAttributes } from "react";

interface SingleImageInputInterface
extends Omit<
InputHTMLAttributes<HTMLInputElement>,
"onChange" | "type" | "capture" | "style" | "name"
> {
onChange: (file: File) => void;
}

const SingleImageInput = ({
onChange,
...others
}: SingleImageInputInterface) => {
return (
<input
name="image"
style={{ display: "none" }}
type="file"
accept="image/*"
capture="environment"
onChange={(e) => {
if (e.target.files) {
onChange(e.target.files[0]);
}
}}
{...others}
/>
);
};

export default SingleImageInput;
7 changes: 6 additions & 1 deletion client/src/components/post/PostCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@ const PostCard = ({
</Typography>
</Stack>

{isMyPost && <PostCardOptionDropdown postId={postNo} />}
{isMyPost && (
<PostCardOptionDropdown
postId={postNo}
filePk={postAttachUrls?.[0]?.attachNo}
/>
)}
</Stack>

{alcoholName && (
Expand Down
31 changes: 20 additions & 11 deletions client/src/components/post/PostCardOptionDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,36 @@ import React, { useState } from "react";
import { MoreVertOutlined } from "@mui/icons-material";
import { ButtonBase, Menu, MenuItem } from "@mui/material";
import { useDeletePostMutation } from "@/queries/post/useDeletePostMutation";
import useDeleteAttachMutation from "@/queries/attach/useDeleteAttachMutation";
import { useRouter } from "next/navigation";
import HOME from "@/const/clientPath";

type PostCardOptionDropdownProps = {
postId: number;
filePk?: string;
};

const PostCardOptionDropdown = ({ postId }: PostCardOptionDropdownProps) => {
const PostCardOptionDropdown = ({
postId,
filePk,
}: PostCardOptionDropdownProps) => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const router = useRouter();

const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const { mutate: deletePost } = useDeletePostMutation();
const { mutateAsync: deletePost } = useDeletePostMutation();
const { mutateAsync: deleteFile } = useDeleteAttachMutation();

const deleteHandler = async () => {
if (confirm("정말 삭제하시겠습니까?")) {
await deletePost(postId);
filePk && (await deleteFile(filePk));
router.push(HOME);
}
};

const handleClose = () => {
setAnchorEl(null);
Expand All @@ -25,15 +42,7 @@ const PostCardOptionDropdown = ({ postId }: PostCardOptionDropdownProps) => {
<MoreVertOutlined />
</ButtonBase>
<Menu open={open} anchorEl={anchorEl} onClose={handleClose}>
<MenuItem
onClick={() => {
if (confirm("정말 삭제하시겠습니까?")) {
deletePost(postId);
}
}}
>
삭제
</MenuItem>
<MenuItem onClick={deleteHandler}>삭제</MenuItem>
<MenuItem>수정</MenuItem>
</Menu>
</>
Expand Down
27 changes: 6 additions & 21 deletions client/src/components/user/info/drawer/UserInfoEditingForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import {
Badge,
} from "@mui/material";
import CameraIcon from "@/assets/icons/badge/CameraIcon.svg";
import { useCallback, useContext, useEffect, useState } from "react";
import { useCallback, useContext, useState } from "react";
import useNewAttachMutation from "@/queries/attach/useNewAttachMutation";
import useDeleteAttachMutation from "@/queries/attach/useDeleteAttachMutation";
import UserPageContext from "@/store/user/UserPageContext";
import CustomAppbar from "@/components/CustomAppbar";
import { useGlobalLoadingStore } from "./../../../../store/useGlobalLoadingStore";
import usePatchUserInfoMutation from "@/queries/user/usePatchUserInfoMutation";
import useRenderAsDataUrl from "@/hooks/useRenderAsDataUrl";
import SingleImageInput from "@/components/SingleImageInput";

const UserInfoEditingForm = () => {
const { setIsEditing } = useContext(UserPageContext);
Expand All @@ -25,8 +27,9 @@ const UserInfoEditingForm = () => {
const { data } = useMyInfoQuery();

const [introduction, setIntroduction] = useState(data?.introduction);

const [file, setFile] = useState<File>();
const [fileUrl, setFileUrl] = useState<string | ArrayBuffer | null>(null);
const fileUrl = useRenderAsDataUrl(file);

const { mutateAsync: attachFile } = useNewAttachMutation();
const { mutateAsync: removeFile } = useDeleteAttachMutation();
Expand Down Expand Up @@ -58,15 +61,6 @@ const UserInfoEditingForm = () => {
setLoading(false);
};

useEffect(() => {
if (!file) {
return;
}
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => setFileUrl(reader.result);
}, [file]);

return (
<>
<CustomAppbar
Expand Down Expand Up @@ -98,16 +92,7 @@ const UserInfoEditingForm = () => {
sx={{ width: 80, height: 80, border: "1px solid #ccc" }}
/>
</Badge>
<input
type="file"
accept="image/*"
onChange={(e) => {
if (e.target.files) {
setFile(e.target.files[0]);
}
}}
style={{ display: "none" }}
/>
<SingleImageInput onChange={(file) => setFile(file)} />
</label>
<Stack gap={2} width="100%">
<Stack direction="row" width="100%">
Expand Down
23 changes: 23 additions & 0 deletions client/src/hooks/useRenderAsDataUrl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useEffect, useState } from "react";

/**
* file 을 입력받아 dataUrl을 리턴하는 훅
* @param file
* @returns fileDataUrl
*/
const useRenderAsDataUrl = (file: File | undefined) => {
const [fileUrl, setFileUrl] = useState<string | ArrayBuffer | null>(null);

useEffect(() => {
if (!file) {
return;
}
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => setFileUrl(reader.result);
}, [file]);

return fileUrl;
};

export default useRenderAsDataUrl;

0 comments on commit be8b4c6

Please sign in to comment.