Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions src/components/Comment/CommentElement.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useState, useEffect } from "react";

const CommentElement = (props) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

props를 변수로 받아 구조분해 할당을 하지 않고, 바로 { comment, handleCommentDelete, handleCommentEdit } 으로 넣어도 괜찮습니다!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const CommentElement = ({ comment, handleCommentDelete, handleCommentEdit })

/* TODO: props 받기
Hint: src/components/Comment/index.jsx에서 어떠한 props를 넘겨주는지 확인해보세요! */
const { comment, handleCommentDelete, handleCommentEdit } = props;

/* TODO: 댓글을 수정하는 input의 value를 관리하기 위한 state 작성
Hint: 댓글의 내용을 저장하는 state와 수정 중인지 여부를 저장하는 state를 따로 만드는 게 좋겠죠? */

const [content, setContent] = useState(comment.content);
const [isEdit, setIsEdit] = useState(false);
// comment created_at 전처리
const date = new Date(comment.created_at);
const year = date.getFullYear();
let month = date.getMonth() + 1;
month = month < 10 ? `0${month}` : month;
let day = date.getDate();
day = day < 10 ? `0${day}` : day;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Date를 사용하서 직접 파싱하는 방법도 있지만, 파싱을 위한 라이브러리를 사용하는 것도 좋습니다.

import { format } from "date-fns";

const formatDate = (dateString) => format(new Date(dateString), "yyyy.MM.dd");

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위 방법이 아니더라도, 이렇게 전역변수를 많이 선언하는 것보다 함수로 분리하는 것이 좋아 보입니다!

const formatDate = (dateString) => {
  const date = new Date(dateString);
  const year = date.getFullYear();
  let month = date.getMonth() + 1;
  let day = date.getDate();

  // 한 자리 수인 경우 앞에 0을 붙여줍니다.
  month = month < 10 ? `0${month}` : month;
  day = day < 10 ? `0${day}` : day;

  return `${year}.${month}.${day}`;
};


const handleEditComment = (e) => {
// 댓글 수정 API 호출
// alert("댓글 수정");
setIsEdit(!isEdit);
handleCommentEdit(comment.id, content);
};

useEffect(() => {
// add api call to check if user is the author of the comment
}, []);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필요없는 코드는 제거하셔도 상관없습니다!


return (
<div className="w-full flex flex-row justify-between items-center mb-5">
<div className="w-3/4 flex flex-col gap-1">
{/* // TODO: 수정 중일 때와 아닐 때를 나눠서 보여줘야 해요!
{수정 중 ? <input ... /> : <p ...>댓글 내용</p>} */}
{isEdit ? (
<input
type="text"
className="input mr-4"
value={content}
onChange={(e) => {
setContent(e.target.value);
}}
/>
) : (
<p className="text-lg mr-4">{comment.content}</p>
)}

<span className="text-base text-gray-300">
{year}.{month}.{day}
</span>
</div>

<div className="flex flex-row items-center gap-3">
{isEdit ? (
<>
<button
className="mr-3"
onClick={() => {
setIsEdit(!isEdit);
setContent(comment.content);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onclick 함수가 길어진다면 따로 분리를 고려해보세요!

const toggleEdit = () => {
    setIsEdit(!isEdit);
    if (isEdit) {
      setContent(comment.content); // 수정 취소 시 원래 내용으로 복원
    }
  };

}}
>
취소
</button>
<button className="mr-3" onClick={handleEditComment}>
완료
</button>
</>
) : (
<>
<button onClick={() => handleCommentDelete(comment.id)}>
삭제
</button>
<button className="mr-3" onClick={() => setIsEdit(!isEdit)}>
수정
</button>
</>
)}
</div>
</div>
);
};
export default CommentElement;
70 changes: 61 additions & 9 deletions src/components/Comment/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,79 @@ import CommentElement from "./CommentElement";

const Comment = ({ postId }) => {
// TODO: comments를 저장하기 위한 state를 만들어주세요
const [commentList, setCommentList] = useState(comments);
// TODO: 새로운 댓글을 추가하기 위한 state를 만들어주세요
const [newContent, setNewContent] = useState("");

const handleCommentSubmit = (e) => {
e.preventDefault();
alert("댓글 작성"); // add api call for creating comment
// TODO: 댓글을 추가했으니 새로운 댓글을 저장하는 state를 초기화해야겠죠?
};

let maxId = 0;
commentList.forEach((comment) => {
if (comment.id > maxId) {
maxId = comment.id;
}
});

const newComment = {
id: maxId + 1,
postId,
content: newContent,
created_at: "2024-04-08T15:09:43Z",
};

setCommentList([...commentList, newComment]);
setNewContent("");
};
const handleCommentDelete = (commentId) => {
console.log(commentId);
alert("댓글 삭제"); // add api call for deleting comment
// console.log(commentId);
// alert("댓글 삭제"); // add api call for deleting comment
const updatedComments = commentList.filter(
(comment) => comment.id !== commentId
);
setCommentList(updatedComments);
};

const handleCommentEdit = (commentId, editedComment) => {
const filteredComment = commentList.map((comment) => {
if (comment.id === commentId) {
comment.content = editedComment;
return comment;
}
return comment;
});
setCommentList(filteredComment);
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

틀린 방법은 아닙니다만...! 아래 방법을 리액트에서는 더 선호합니다. 데이터의 직접 변경은 종종 예상치 못한 부작용을 일으킬 수 있습니다. 예를 들어, 객체나 배열을 여러 컴포넌트에서 참조하고 있을 때, 한 곳에서 객체를 직접 변경하면 다른 모든 참조에서도 그 변경 사항이 반영됩니다. 이는 예상치 못한 버그를 유발할 수 있습니다. 불변성을 유지하면 이러한 문제를 방지할 수 있습니다.

const updatedComments = commentList.map((comment) => {
    if (comment.id === commentId) {
      // 불변성을 유지하며 수정된 새 객체 생성
      return { ...comment, content: editedContent };
    }
    return comment;
  });
  setCommentList(updatedComments);

return (
<div className="w-full mt-5 self-start">
<h1 className="text-3xl font-bold my-5">Comments</h1>
// TODO: comments 더미데이터를 돌며 각 댓글마다 CommentElement를
만들어주세요! // Hint: CommentElement에는 댓글인 comment와 댓글 삭제를
{/* // TODO: comments 더미데이터를 돌며 각 댓글마다 CommentElement를 만들어주세요! // Hint: CommentElement에는 댓글인 comment와 댓글 삭제를
하는 handleCommentDelete라는 props가 필요합니다. // TODO: 새로운 댓글을
작성하는 form을 만들어주세요!
<form className="flex flex-row items-center justify-center mt-10 gap-2"></form>
작성하는 form을 만들어주세요! */}
{commentList.map((comment) => (
<CommentElement
comment={comment}
key={comment.id}
handleCommentDelete={handleCommentDelete}
handleCommentEdit={handleCommentEdit}
/>
))}
<form
className="flex items-center justify-center mt-10 gap-2"
onSubmit={handleCommentSubmit}
>
<input
type="text"
value={newContent}
placeholder="댓글을 입력해주세요"
className="input h-14"
onChange={(e) => setNewContent(e.target.value)}
/>

<button type="submit" className="button flex-none ">
작성
</button>
</form>
</div>
);
};
Expand Down
1 change: 1 addition & 0 deletions src/components/Header/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const Header = () => {
</Link>
<div className="flex">
{isUserLoggedIn ? (

<Link to="/" className="mr-10 p-3 uppercase text-lg">
sign out
</Link>
Expand Down
6 changes: 5 additions & 1 deletion src/routes/HomePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ const HomePage = () => {
setTags([...tagList]);
setSearchTags([...tagList]);
}, []);
const handleChange = (e) => {};
const handleChange = (e) => {
const { value } = e.target;
const newTags = tags.filter((tag) => tag.includes(value));
setSearchTags(newTags);
};
const handleTagFilter = (e) => {
const { innerText } = e.target;
if (searchValue === innerText.substring(1)) {
Expand Down
13 changes: 10 additions & 3 deletions src/routes/PostDetailPage.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import { useState, useEffect } from "react";
import { useParams, Link } from "react-router-dom";

import { useParams, Link, useNavigate } from "react-router-dom";
import Comment from "../components/Comment";
import { BigPost } from "../components/Posts";
import posts from "../data/posts";

const PostDetailPage = () => {
const { postId } = useParams();

const [post, setPost] = useState(null);

useEffect(() => {
const post = posts.find((post) => post.id === parseInt(postId));
setPost(post);
}, [postId]);

const navigate = useNavigate();
const onClickDelete = () => {
alert("삭제");
//TODO : api connect(delete post)
alert("게시물을 삭제합니다.");
navigate("/");
// add api call for deleting post
};

return (
post && (
<div className="flex flex-col items-center w-[60%] p-8">
<BigPost post={post} />
<Comment postId={postId} />
<div className="flex flex-row gap-3">
<Link to={`/${post.id}/edit`}>
<button className="button mt-10 py-2 px-10">수정</button>
Expand Down
Loading