댓글이 첫번째 포스트카드에만 붙습니다.
미해결
React로 NodeBird SNS 만들기
원인파악이 잘안되네요.. post.id가 카드를 생성해도 계속 1로만 있는것 같은데 이유를 잘 모르겠습니다. 아래 코드인데 한번 봐주실 수 있을까요.. 아니면 디버깅 방법이라도 알려주시면 감사하겠습니다. 선택한카드에 댓글버턴을 눌렀을때 선택한 카드를 콘솔에 찍는방법이 잘 안떠오릅니다. 콘솔에는 아래와 같은 에러가 뜹니다. Warning: Encountered two children with the same key, `[object Object]`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version. in Home (at _app.js:24) in div (created by Context.Consumer) in Col (at AppLayout.js:54) in div (created by Context.Consumer) in Row (at AppLayout.js:50) in AppLayout (at _app.js:23) in Provider (at _app.js:15) in NodeBird (created by withRedux(NodeBird)) in withRedux(NodeBird) in Container (created by AppContainer) in AppContainer ***** postcard.js ================== import React, { useState, useCallback, useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { ADD_COMMENT_REQUEST } from "../reducers/post"; import { Card, Button, Avatar, Input, Comment, List, Form } from "antd"; import { RetweetOutlined, HeartOutlined, MessageOutlined, EllipsisOutlined, } from "@ant-design/icons"; const PostCard = ({ post }) => { const [commentFormOpened, setCommentFormOpened] = useState(false); const [commentText, setCommentText] = useState(""); const { me } = useSelector((state) => state.user); const { isAddedComment, isAddingComment } = useSelector( (state) => state.post ); const dispatch = useDispatch(); useEffect(() => { console.log("effect"); setCommentText(""); }, [isAddedComment === true]); const onCommentToggle = useCallback(() => { setCommentFormOpened((prev) => !prev); }, []); const onChangeCommentText = useCallback((e) => { setCommentText(e.target.value); }, []); const onSubmitComment = useCallback(() => { if (!me) { return alert("Please Login First"); } console.log("submit"); console.log(post.id); return dispatch({ type: ADD_COMMENT_REQUEST, data: { postId: post.id } }); }, [me && me.id]); return ( <div> <Card key={+post.createdAt} hoverable style={{ width: 240, padding: 10, marginTop: 10 }} cover={post.img && <img alt={post} src={post.img} />} actions={[ <RetweetOutlined />, <HeartOutlined />, <MessageOutlined onClick={onCommentToggle} />, <EllipsisOutlined />, ]} extra={<Button>Delete</Button>} > <Card.Meta title={post.User.nickName} description={post.content} ></Card.Meta> </Card> {commentFormOpened && ( <React.Fragment> <Form onFinish={onSubmitComment}> <Form.Item> <Input.TextArea rows={4} value={commentText} onChange={onChangeCommentText} /> </Form.Item> <Button type="primary" htmlType="submit" loading={isAddingComment}> Reply </Button> </Form> <List header={`${post.Comments ? post.Comments.length : 0} 댓글`} itemLayout="horizontal" dataSource={post.Comments || []} renderItem={(item) => ( <li> <Comment author={item.User.nickName} avatar={<Avatar>{item.User.nickName[0]}</Avatar>} content={item.content} /> </li> )} /> </React.Fragment> )} </div> ); }; export default PostCard; ***** index.js================== import React from "react"; import Postform from "../components/postform"; import PostCard from "../components/postcard"; import { useSelector } from "react-redux"; const Home = () => { const { mainPosts } = useSelector((state) => state.post); const { isLogged } = useSelector((state) => state.user); return ( <React.Fragment> {isLogged && <Postform />} {mainPosts.map((v) => { return <PostCard key={v} post={v} />; })} </React.Fragment> ); }; export default Home; ***** sagas/post.js ================== import { delay, fork, all, takeLatest, put } 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"; // ACTION_API, ACTION_NAME, ACTION_WATCH are set function* addPostAPI() { // return axios.post('/post') } function* addPost() { try { yield delay(2000); yield put({ type: ADD_POST_SUCCESS }); } catch (e) { console.log(e); yield put({ type: ADD_POST_FAILURE, postErrorReason: e }); } } // add Comment function* addPostWatch() { yield takeLatest(ADD_POST_REQUEST, addPost); } function* addCommentAPI() { // return axios.post('/post') } function* addComment(action) { try { yield delay(2000); yield put({ type: ADD_COMMENT_SUCCESS, data: { postId: action.data.postId }, }); } catch (e) { console.log(e); yield put({ type: ADD_COMMENT_FAILURE, commentErrorReason: e }); } } function* addCommentWatch() { yield takeLatest(ADD_COMMENT_REQUEST, addComment); // dispatch할때 action값이 addComment로 넘어가는듯 } export default function* postSaga() { yield all([fork(addPostWatch), fork(addCommentWatch)]); } ***** reducer/post.js ================== 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"; export const initialState = { mainPosts: [ { id: 1, User: { id: 1, nickName: "", }, content: "", img: "https://img.icons8.com/plasticine/2x/image.png", Comments: [], }, ], imagePaths: [], isAddingPost: false, isAddedPost: false, postErrorReason: "", isAddingComment: false, isAddedComment: false, commentErrorReason: "", }; const dummyPost = { id: 2, User: { id: 1, nickName: "dum cho", }, content: "dummy Post", Comments: [], img: "https://img.icons8.com/plasticine/2x/image.png", }; const dummyComment = { id: 1, User: { id: 1, nickName: "dum cho", }, Comments: "this is dummy", }; const reducer = (state = initialState, action) => { switch (action.type) { case ADD_POST_REQUEST: return { ...state, mainPost: action.data, isAddedPost: false, isAddingPost: true, }; case ADD_POST_SUCCESS: return { ...state, mainPosts: [dummyPost, ...state.mainPosts], isAddedPost: true, isAddingPost: false, }; case ADD_POST_FAILURE: return { ...state, mainPost: action.data, isAddedPost: false, isAddingPost: false, }; case ADD_COMMENT_REQUEST: return { ...state, isAddedComment: false, isAddingComment: true, }; case ADD_COMMENT_SUCCESS: { const postIndex = state.mainPosts.findIndex( (v) => v.id === action.data.postId ); // console.log(action.data.postId, postIndex); const post = state.mainPosts[postIndex]; const Comments = [...post.Comments, dummyComment]; const mainPosts = [...state.mainPosts]; mainPosts[postIndex] = { ...post, Comments }; return { ...state, mainPosts, isAddedComment: true, isAddingComment: false, }; } case ADD_COMMENT_FAILURE: return { ...state, mainPost: action.data, isAddedComment: false, commentErrorReason: false, }; default: return { ...state }; } }; export default reducer;
- react
- javascript





