1. 댓글 작성하기 - <PostDetail /> 고치기
더보기
👉 댓글을 작성기능을 만들 때 어떤 순서로 만들면 될까요?
→ 댓글 작성하는 뷰를 만들고, 댓글 텍스트를 리덕스에 넣는 게 첫번째!
그 다음으로는 파이어스토어에 저장하는 기능을 만들어서 진짜 저장까지 해주면 됩니다.
그런데, 우리 데이터를 보면 게시글 정보에도 댓글 갯수가 저장되고 있죠!
앗, 그럼 댓글을 작성하면 게시글 정보에 있는 댓글 갯수도 +1 해줘야겠네요!
댓글을 작성하면서 게시글 정보도 같이 수정하도록 해봅시다!
- <PostDetail /> 고치기 (firestore 데이터를 리덕스에 넣어 두도록)
1) post.js에서 게시글 하나 정보 가져오는 함수 만들기
...
const getOnePostFB = (id) => {
return function(dispatch, getState, {history}){
const postDB = firestore.collection("post");
postDB
.doc(id)
.get()
.then((doc) => {
let _post = doc.data();
if (!_post) {
return;
}
let post = Object.keys(_post).reduce(
(acc, cur) => {
if (cur.indexOf("user_") !== -1) {
return {
...acc,
user_info: { ...acc.user_info, [cur]: _post[cur] },
};
}
return { ...acc, [cur]: _post[cur] };
},
{ id: doc.id, user_info: {} }
);
dispatch(setPost([post], { start: null, next: null, size: 3 }));
});
}
}
...
export default handleActions(
{
[SET_POST]: (state, action) =>
produce(state, (draft) => {
draft.list.push(...action.payload.post_list);
// post_id가 같은 중복 항목을 제거합시다! :)
draft.list = draft.list.reduce((acc, cur) => {
// findIndex로 누산값(cur)에 현재값이 이미 들어있나 확인해요!
// 있으면? 덮어쓰고, 없으면? 넣어주기!
if (acc.findIndex((a) => a.id === cur.id) === -1){
return [...acc, cur];
}else{
acc[acc.findIndex((a) => a.id === cur.id)] = cur;
return acc;
}
}, []);
// paging이 있을 때만 넣기
if (action.payload.paging) {
draft.paging = action.payload.paging;
}
draft.is_loading = false;
}),
...
},
initialState
);
const actionCreators = {
setPost,
addPost,
editPost,
getPostFB,
addPostFB,
editPostFB,
getOnePostFB,
};
...
2) 상세 페이지에 적용하기
import React from "react";
import Post from "../components/Post";
import CommentList from "../components/CommentList";
import CommentWrite from "../components/CommentWrite";
import { useSelector, useDispatch } from "react-redux";
import {actionCreators as postActions} from "../redux/modules/post";
const PostDetail = (props) => {
const dispatch = useDispatch();
const id = props.match.params.id;
const user_info = useSelector((state) => state.user.user);
const post_list = useSelector((store) => store.post.list);
const post_idx = post_list.findIndex((p) => p.id === id);
const post = post_list[post_idx];
React.useEffect(() => {
if (post) {
return;
}
dispatch(postActions.getOnePostFB(id));
}, []);
return (
<React.Fragment>
{post && (
<Post {...post} is_me={post.user_info.user_id === user_info?.uid} />
)}
<CommentWrite post_id={id} />
<CommentList post_id={id} />
</React.Fragment>
);
};
export default PostDetail;
3) 게시글 목록 페이지 첫 진입 시, 데이터 가져오는 조건 바꿔주기
// pages/PostList.js
...
React.useEffect(() => {
// 가지고 있는 데이터가 0개, 1개일 때만 새로 데이터를 호출해요.
if (post_list.length < 2) {
dispatch(postActions.getPostFB());
}
}, []);
...
2. 댓글 가져오기
- 게시글 id와, 작성 일시 역순으로 정렬해서 가져오기
const getCommentFB = (post_id = null) => {
return function (dispatch, getState, { history }) {
const commentDB = firestore.collection("comment");
// post_id가 없으면 바로 리턴하기!
if(!post_id){
return;
}
// where로 게시글 id가 같은 걸 찾고,
// orderBy로 정렬해줍니다.
commentDB
.where("post_id", "==", post_id)
.orderBy("insert_dt", "desc")
.get()
.then((docs) => {
let list = [];
docs.forEach((doc) => {
list.push({ ...doc.data(), id: doc.id });
});
// 가져온 데이터를 넣어주자!
dispatch(setComment(post_id, list));
}).catch(err => {
console.log("댓글 가져오기 실패!", post_id, err);
});
};
};
- 리듀서에 적용하기
[SET_COMMENT]: (state, action) =>
produce(state, (draft) => {
// comment는 딕셔너리 구조로 만들어서,
// post_id로 나눠 보관합시다! (각각 게시글 방을 만들어준다고 생각하면 구조 이해가 쉬워요.)
draft.list[action.payload.post_id] = action.payload.comment_list;
}),
- <CommentList/>에서 부르기
// components/CommentList.js
import React from "react";
import {Grid, Image, Text} from "../elements";
import {useDispatch, useSelector} from "react-redux";
import {actionCreators as commentActions} from "../redux/modules/comment";
const CommentList = (props) => {
const dispatch = useDispatch();
const comment_list = useSelector(state => state.comment.list);
const {post_id} = props;
React.useEffect(() => {
if(!comment_list[post_id]){
// 코멘트 정보가 없으면 불러오기
dispatch(commentActions.getCommentFB(post_id));
}
}, []);
// comment가 없거나, post_id가 없으면 아무것도 안넘겨준다!
if(!comment_list[post_id] || !post_id){
return null;
}
return (
<React.Fragment>
<Grid padding="16px">
{comment_list[post_id].map(c => {
return (<CommentItem key={c.id} {...c}/>);
})}
</Grid>
</React.Fragment>
);
};
CommentList.defaultProps = {
post_id: null
};
export default CommentList;
const CommentItem = (props) => {
const {user_profile, user_name, user_id, post_id, contents, insert_dt} = props;
return (
<Grid is_flex>
<Grid is_flex width="auto">
<Image shape="circle"/>
<Text bold>{user_name}</Text>
</Grid>
<Grid is_flex margin="0px 4px">
<Text margin="0px">{contents}</Text>
<Text margin="0px">{insert_dt}</Text>
</Grid>
</Grid>
)
}
CommentItem.defaultProps = {
user_profile: "",
user_name: "mean0",
user_id: "",
post_id: 1,
contents: "귀여운 고양이네요!",
insert_dt: '2021-01-01 19:00:00'
}
3. 댓글 추가하기
- 댓글 추가하기
1) 댓글 입력하면 내용 가져오기
// components/CommentWrite.js
import React from "react";
import Post from "../components/Post";
import CommentList from "../components/CommentList";
import CommentWrite from "../components/CommentWrite";
import { useSelector } from "react-redux";
import { firestore } from "../shared/firebase";
const PostDetail = (props) => {
...
const [contents, setContents] = React.useState("");
...
const write = () => {
if(contents === ""){
window.alert("댓글을 입력해주세요!");
return;
}
console.log(comment_text);
}
return (
<React.Fragment>
{post && (
<Post {...post} is_me={post.user_info.user_id === user_info.uid} />
)}
<CommentWrite
_onChange={(e) => {
setContents(e.target.value);
}}
_onClick={write}
/>
<CommentList />
</React.Fragment>
);
};
export default PostDetail;
2) addCommentFB를 만든다.
const addCommentFB = (post_id, contents) => {
return function (dispatch, getState, { history }) {
const commentDB = firestore.collection("comment");
const user_info = getState().user.user;
let comment = {
post_id: post_id,
user_id: user_info.uid,
user_name: user_info.user_name,
user_profile: user_info.user_profile,
contents: contents,
insert_dt: moment().format("YYYY-MM-DD hh:mm:ss"),
};
// firestore에 코멘트 정보를 넣어요!
commentDB.add(comment).then((doc) => {
const postDB = firestore.collection("post");
comment = { ...comment, id: doc.id };
const post = getState().post.list.find((l) => l.id === post_id);
// firestore에 저장된 값을 +1해줍니다!
const increment = firebase.firestore.FieldValue.increment(1);
// post에도 comment_cnt를 하나 플러스 해줍니다.
postDB
.doc(post_id)
.update({ comment_cnt: increment })
.then((_post) => {
dispatch(addComment(post_id, comment));
// 리덕스에 post가 있을 때만 post의 comment_cnt를 +1해줍니다.
if (post) {
dispatch(
postActions.editPost(post_id, {
comment_cnt: parseInt(post.comment_cnt) + 1,
})
);
}
});
});
};
};
3) 리듀서에 적용하기
[ADD_COMMENT]: (state, action) => produce(state, (draft) => {
draft.list[action.payload.post_id].push(action.payload.comment);
}),
4) <CommentWrite/>에서 부르기
const write = () => {
if (comment_text === "") {
window.alert("댓글을 입력해주세요!");
return;
}
// 파이어스토어에 추가합니다.
dispatch(commentActions.addCommentFB(post_id, comment_text));
// 입력된 텍스트는 지우기!
setCommentText("");
};
5) 댓글 입력 후 입력한 텍스트 지우기
// elements/Input.js
...
<ElInput
type={type}
placeholder={placeholder}
onChange={_onChange}
value={value}
/>
...
'항해 중 > 5주차 리액트 심화반' 카테고리의 다른 글
4주차 - 엔터 키로 입력하기 (1) | 2021.12.03 |
---|---|
4주차 - 댓글 알림 뱃지 만들기 (0) | 2021.12.03 |
4주차 - 상세 페이지 연결하기 + fireStore 복합 쿼리 (0) | 2021.12.02 |
4주차 - 무한 스크롤 (0) | 2021.12.02 |
3주차 - Debounce, Throttle (0) | 2021.12.01 |
댓글