인프런 커뮤니티 질문&답변
대댓글 작성시 오류 발생
작성
·
521
답변 4
0
0
이승주
질문자
<sagas_post.js>
import axios from "axios";
import { delay, put, takeLatest, all, fork } from "redux-saga/effects";
import {
ADD_POST_REQUEST,
ADD_POST_SUCCESS,
ADD_POST_FAILURE,
ADD_COMMENT_REQUEST,
ADD_COMMENT_SUCCESS,
ADD_COMMENT_FAILURE,
} from "../reducers/post";
function addPostAPI(data) {
return axios.post("/api/post", data);
}
//put은 디스패치
function* addPost(action) {
try {
//call은 데이터 나올떄까지 대기
yield delay(1000);
// const result = yield call(addPostAPI, action.data);
yield put({
type: ADD_POST_SUCCESS,
data: action.data,
});
} catch (err) {
yield put({
type: ADD_POST_FAILURE,
data: err.response.data,
});
}
}
function addCommentAPI(data) {
return axios.post("/api/post/${data.postId}/comment", data);
}
//put은 디스패치
function* addComment(action) {
try {
//call은 데이터 나올떄까지 대기
yield delay(1000);
// const result = yield call(addPostAPI, action.data);
console.log("addComment사가");
yield put({
type: ADD_COMMENT_SUCCESS,
data: action.data,
});
} catch (err) {
yield put({
type: ADD_COMMENT_FAILURE,
data: err.response.data,
});
}
}
function* watchAddPost() {
yield takeLatest(ADD_POST_REQUEST, addPost);
}
function* watchAddComment() {
yield takeLatest(ADD_COMMENT_REQUEST, addComment);
}
export default function* postSaga() {
yield all([fork(watchAddPost), fork(watchAddComment)]);
}
0
이승주
질문자
<reducer_post.js>
import shortId from "shortid";
export const initialState = {
mainPosts: [
{
id: 1,
User: {
id: 1,
nickname: "제로초",
},
content: "첫번째 게시글#해시테그#익스프레스",
Images: [
{
src: "https://bookthumb-phinf.pstatic.net/cover/137/995/13799585.jpg?update=20180726",
},
{
src: "https://gimg.gilbut.co.kr/book/BN001958/rn_view_BN001958.jpg",
},
{
src: "https://gimg.gilbut.co.kr/book/BN001998/rn_view_BN001998.jpg",
},
],
Comments: [
{
User: {
nickname: "nero",
},
content: "우와 개정판이당",
},
{
User: {
nickname: "hero",
},
content: "얼른사고 싶어요~",
},
],
imagePaths: [],
postAdded: false,
addPostLoading: false,
addPostDone: false,
addPostError: null,
addCommentLoading: false,
addCommentDone: false,
addCommentError: null,
},
],
};
//액션 이름을 상수로 뺌
export const ADD_POST_REQUEST = "ADD_POST_REQUEST";
export const ADD_POST_SUCCESS = "ADD_POST_SUCCESS";
export const ADD_POST_FAILURE = "ADD_POST_FAILURE";
export const ADD_COMMENT_REQUEST = "ADD_COMMENT_REQUEST";
export const ADD_COMMENT_SUCCESS = "ADD_COMMENT_SUCCESS";
export const ADD_COMMENT_FAILURE = "ADD_COMMENT_FAILURE";
const ADD_POST = "ADD_POST";
export const addPost = (data) => ({
type: ADD_POST_REQUEST,
data,
});
export const addComment = (data) => ({
type: ADD_COMMENT_REQUEST,
data,
});
const dummyPost = (data) => ({
id: shortId.generate(),
content: data,
User: {
id: 1,
nickname: "제로초",
},
Images: [],
Comments: [],
});
const dummyComment = (data) => ({
id: shortId.generate(),
content: data,
User: {
id: 1,
nickname: "제로초",
},
});
const reducer = (state = initialState, action) => {
switch (action.type) {
case ADD_POST_REQUEST:
return {
...state,
addPostLoading: true,
addPostDone: false,
addPostError: null,
};
case ADD_POST_SUCCESS:
return {
...state,
// dummyPost앞에 작성해야 게시글 위에 올라감
mainPosts: [dummyPost(action.data), ...state.mainPosts],
addPostLoading: false,
addPostDone: true,
};
case ADD_POST_FAILURE:
return {
...state,
addPostLoading: false,
addPostError: action.error,
};
case ADD_COMMENT_REQUEST:
const postIndex = state.mainPosts.findIndex(
(v) => v.id === action.data.postId
);
const post = { ...state.mainPosts[postIndex] };
post.Comments = [dummyComment(action.data.content), ...post.Comments];
const mainPosts = [...state.mainPosts];
mainPosts[postIndex] = post;
return {
...state,
mainPosts,
addCommentLoading: false,
addCommentDone: true,
};
case ADD_COMMENT_SUCCESS:
return {
...state,
// dummyPost앞에 작성해야 게시글 위에 올라감
mainPosts: [dummyPost, ...state.mainPosts],
addCommentLoading: false,
addCommentDone: true,
};
case ADD_COMMENT_FAILURE:
return {
...state,
addCommentLoading: false,
addCommentError: action.error,
};
default:
return state;
}
};
export default reducer;
0
이승주
질문자
<components_PostCard.js>
import React, { useState, useCallback } from "react";
import PropTypes from "prop-types";
import { useSelector, useDispatch } from "react-redux";
import { Card, Popover, Button, Avatar, List, Comment } from "antd";
import {
RetweetOutlined,
HeartOutlined,
MessageOutlined,
EllipsisOutlined,
HeartTwoTone,
ConsoleSqlOutlined,
} from "@ant-design/icons";
import Link from "next/link";
import moment from "moment";
import CommentForm from "./CommentForm";
import PostImages from "./PostImages";
import PostCardContent from "./PostCardContent";
const PostCard = ({ post }) => {
console.log("포스트카드");
console.log(post);
const [Liked, setLiked] = useState(false);
const [commentFormOpened, setCommentFormOpened] = useState(false);
const onToggleLike = useCallback(() => {
// prev는 자동으로 이전 데이터가 들어가 있음. true는 false로 false는 true로
setLiked((prev) => !prev);
}, []);
const onToggleComment = useCallback(() => {
// true는 false로 false는 true로
setCommentFormOpened((prev) => !prev);
}, []);
// const { id } = useSelector((state) => state.user.me && state.user.me.id);
// 같은 문장인데 줄어 든거임.
const id = useSelector((state) => state.user.me?.id);
return (
<div style={{ marginTop: 20 }}>
<Card
cover={post.Images[0] && <PostImages images={post.Images} />}
// 배열 안에 jsx를 넣어줄거면 key를 넣어 줘야 한다.
actions={[
<RetweetOutlined key="retweet" />,
Liked ? (
<HeartTwoTone
twoToneColor="#eb2f96"
key="heart"
onClick={onToggleLike}
/>
) : (
<HeartOutlined key="heart" onClick={onToggleLike} />
),
<MessageOutlined key="comment" onClick={onToggleComment} />,
<Popover
key="more"
content={
<Button.Group>
{/* 로그린 했고 작성자 아이디랑 내아이디랑 같으면 수정 가능 */}
{id && post.User.id === id ? (
<>
<Button>수정</Button>
<Button type="danger">삭제</Button>
</>
) : (
<Button>신고</Button>
)}
</Button.Group>
}
>
<EllipsisOutlined />
</Popover>,
]}
>
<Card.Meta
// 유저 닉네임의 첫번째 글자
avatar={<Avatar>{post.User.nickname[0]}</Avatar>}
title={post.User.nickname}
description={<PostCardContent postData={post.content} />}
/>
</Card>
{commentFormOpened && (
<div>
<CommentForm post={post} />
<List
header={`${post.Comments.length}개의 댓글`}
itemLayout="horizontal"
dataSource={post.Comments}
// post.comments의 세부 항목이 item으로 들어감
renderItem={(item) => (
<li>
<Comment
author={item.User.nickname}
avatar={<Avatar>{item.User.nickname[0]}</Avatar>}
content={item.content}
/>
</li>
)}
/>
</div>
)}
{/* <CommentForm />
<Comments /> */}
</div>
);
};
PostCard.propTypes = {
// PropTypes.object이렇게 할 수 있는데 더 구체적으로 적어 줄수 있다.
post: PropTypes.shape({
id: PropTypes.number,
User: PropTypes.object,
content: PropTypes.string,
createdAt: PropTypes.object,
Comments: PropTypes.arrayOf(PropTypes.object),
Images: PropTypes.arrayOf(PropTypes.string),
}).isRequired,
};
export default PostCard;






