1. firebase database -> post collection -> like_cnt, like_list(해당 게시물의 좋아요를 누른 사람 목록) 추가
2. 좋아요 뷰 만들기
post_list에서 좋아요를 눌렀을 때 색상 변하는 방법을 설명하기 전에
좋아요 기능을 넣기 전의 코드에서는 PostList.js에서 Post 컴포넌트에 onClick을 사용해서 /postDetail로 보냈는 데
(즉, 게시물 목록에서 게시물을 누를 경우 상세 페이지로 이동 시켰는 데)
그럴 경우 좋아요를 누를 수 없어서 게시물 목록(PostList.js)에 있는 onClick을 Post.js에서 contents와 image에만 적용 되도록 바꿨다.
방법 1)
- state에 좋아요 유무를 boolean 값을 넣는다.
- state에 좋아요 유무에 따라 string 값을 넣는다.
- styled-components를 사용해서 Like를 만들고, styles를 props로 넘긴다.
- Like에서는 props로 받아온 color와 삼항 조건식을 사용해서 좋아요를 누른 상태라면 pink를 주고, 좋아요를 해제한 상태라면 gray를 부여한다.
// Post.js
import React from "react";
import { Grid, Image, Text, Button } from "../elements";
import { history } from "../redux/configureStore";
import styled from 'styled-components'
const Post = (props) => {
const [like, setLike] = React.useState(false); // ***
const [color, setColor] = React.useState('unLike'); // ***
const styles = {color : color}; // ***
const likeClick = () => { // ***
if(like){
setLike(false)
setColor('unLike');
// 좋아요를 누르면 firebase, redux에 like_cnt + 1
}else{
setLike(true)
setColor('like')
// 좋아요 해제하면 firebase, redux에 like_cnt - 1
}
}
return (
<React.Fragment>
<Grid>
<Grid is_flex padding="16px">
<Grid is_flex width="auto">
<Image shape="circle" src={props.user_profile}></Image>
<Text bold>{props.user_info.user_name}</Text>
</Grid>
<Grid is_flex width="auto"> // *******
<Text>{props.insert_dt}</Text>
{props.is_me && (
<Button
width="auto"
padding="4px"
margin="4px"
_onClick={() => {
history.push(`/postWrite/${props.id}`);
}}
>
수정
</Button>
)}
</Grid>
</Grid>
<Grid
_onClick={() => {
history.push(`postDetail/${props.id}`);
}}
>
<Grid padding="16px">
<Text>{props.contents}</Text>
</Grid>
<Grid>
<Image shape="rectangle" src={props.image_url}></Image>
</Grid>
</Grid>
<Grid padding="16px" is_flex>
<Text bold>댓글 {props.comment_cnt}개</Text>
{/* 좋아요 유무 */}
<Like {...styles} onClick={likeClick}>♥</Like> // ***
</Grid>
</Grid>
</React.Fragment>
);
};
Post.defaultProps = {
user_info: {
user_name: "오새봄",
user_profile: "https://i.ytimg.com/vi/Ct1Pp_4FEIY/maxresdefault.jpg",
},
image_url: "https://i.ytimg.com/vi/Ct1Pp_4FEIY/maxresdefault.jpg",
contents: "오새봄이다. 찬양해라",
comment_cnt: 10,
insert_dt: "2021-11-29 19:09:00",
};
const Like = styled.div` // ***
font-size : 30px;
color : ${(props) => (props.color === 'like')? 'pink' : 'gray'};
`
export default Post;
방법 2)
- state에 좋아요 유무를 boolean 값을 넣는다.
- 삼항 조건식을 사용해서 true이면 꽉 찬 하트를 보여주고 false이면 빈 하트를 보여준다.
// Post.js
import React from "react";
import { Grid, Image, Text, Button } from "../elements";
import { history } from "../redux/configureStore";
import styled from 'styled-components'
const Post = (props) => {
console.log(props);
const [like, setLike] = React.useState(true); // ***
const likeClick = () => { // ***
if(like){
setLike(false)
// 좋아요를 누르면 firebase, redux에 like_cnt + 1
}else{
setLike(true)
// 좋아요 해제하면 firebase, redux에 like_cnt - 1
}
}
return (
<React.Fragment>
<Grid>
<Grid is_flex padding="16px">
<Grid is_flex width="auto">
<Image shape="circle" src={props.user_profile}></Image>
<Text bold>{props.user_info.user_name}</Text>
</Grid>
<Grid is_flex width="auto">
<Text>{props.insert_dt}</Text>
{props.is_me && (
<Button
width="auto"
padding="4px"
margin="4px"
_onClick={() => {
history.push(`/postWrite/${props.id}`);
}}
>
수정
</Button>
)}
</Grid>
</Grid>
<Grid
_onClick={() => {
history.push(`postDetail/${props.id}`); // *******
}}
>
<Grid padding="16px">
<Text>{props.contents}</Text>
</Grid>
<Grid>
<Image shape="rectangle" src={props.image_url}></Image>
</Grid>
</Grid>
<Grid padding="16px" is_flex>
<Text bold>댓글 {props.comment_cnt}개</Text>
{/* 좋아요 유무 */}
{like ? ( // ***
<Like size="20px" bold onClick={likeClick}>
♥
</Like>
) : (
<Like size="20px" bold onClick={likeClick}>
♡
</Like>
)}
</Grid>
</Grid>
</React.Fragment>
);
};
Post.defaultProps = {
user_info: {
user_name: "오새봄",
user_profile: "https://i.ytimg.com/vi/Ct1Pp_4FEIY/maxresdefault.jpg",
},
image_url: "https://i.ytimg.com/vi/Ct1Pp_4FEIY/maxresdefault.jpg",
contents: "오새봄이다. 찬양해라",
comment_cnt: 10,
insert_dt: "2021-11-29 19:09:00",
};
const Like = styled.div`
font-size : 30px;
`
export default Post;
3. 좋아요 기능 모듈
import { createAction, handleActions } from "redux-actions";
import { produce } from "immer";
import { firestore } from "../../shared/firebase";
import firebase from "firebase/compat/app";
// 액션 타입
const MINUS_LIKE = "MINUS_LIKE";
const ADD_LIKE = "ADD_LIKE";
// 액션 생성 함수
const addLike = createAction(ADD_LIKE, (post_id, like_cnt) => ({
post_id,
like_cnt,
}));
const minusLike = createAction(MINUS_LIKE, (post_id, like_cnt) => ({
post_id,
like_cnt,
}));
// 초기값
const initialState = {
post_id: null,
like_cnt: 0,
};
// 미들웨어
const addLikeFB = (post_id = null, like_cnt, like_list) => {
return function (dispatch, getState, { history }) {
const postDB = firestore.collection("post");
const user_id = getState().user.user.uid; // 사용자 uid
const increment = firebase.firestore.FieldValue.increment(1);
// 인자에 들어간 값만큼 현재 값에서 추가 해준다.
postDB
.doc(post_id)
.update({ like_cnt: increment, like_list: [...like_list, user_id] })
.then((docs) => {
window.alert("좋아요를 누르셨습니다.");
window.location.reload();
});
};
};
const minusLikeFB = (post_id = null, like_cnt, like_list) => {
return function (dispatch, getState, { history }) {
const postDB = firestore.collection("post");
const user_id = getState().user.user.uid; // 사용자 uid
const increment = firebase.firestore.FieldValue.increment(-1);
// 인자에 들어간 값만큼 현재 값에서 추가 해준다.
const new_like_list = like_list.filter((l) => {
return l !== user_id;
});
postDB
.doc(post_id)
.update({ like_cnt: increment, like_list: new_like_list })
.then((docs) => {
window.alert("좋아요를 취소하셨습니다.");
window.location.reload();
});
};
};
// 리듀서
export default handleActions(
{
[ADD_LIKE]: (state, action) => {
produce(state, (draft) => {});
},
[MINUS_LIKE]: (state, action) => {
produce(state, (draft) => {});
},
},
initialState
);
// 액션 생성 함수 export
const actionCreators = {
addLikeFB,
minusLikeFB,
};
export { actionCreators };
4. Post.js 최종
// Post.js
import React from "react";
import { Grid, Image, Text, Button } from "../elements";
import { history } from "../redux/configureStore";
import styled from "styled-components";
import Permit from "../shared/Permit";
import { useSelector, useDispatch } from "react-redux";
import { actionCreators as likeActions } from "../redux/modules/like";
const Post = (props) => {
const user_id = useSelector((state) => state.user.user?.uid);
const post = useSelector((state) => state.post.list);
const [like, setLike] = React.useState(false);
const [color, setColor] = React.useState("unLike");
const styles = { color: color };
const dispatch = useDispatch();
// 좋아요 유무에 따라서 새로고침이 되어도 그대로 반영 되게 하는 코드
React.useEffect(()=>{
console.log(post)
const _post = post.filter((p) => {
// 게시물 정보에서 좋아요를 누른 사람의 목록을 가져온다.
return p.id === props.id;
})[0].like_list;
if (_post) {
_post.forEach((p) => {
if (p === user_id) {
setLike(true);
setColor('like');
}
});
}
})
const likeClick = (props) => {
const post_id = props.id; // 게시물 정보
const like_cnt = props.like_cnt; // 좋아요 갯수
const post_user_id = props.user_info.user_id; // 게시물 작성자
const like_list = props.like_list; // 좋아요 누른 사람들 아이디
// 게시물 작성자인 지 체크
if (post_user_id === user_id) {
window.alert("작성자의 게시물에는 좋아요를 누르실 수 없습니다.");
return;
}
// else if(like_list.includes(user_id)){ // 이게 진짜 필요할까??
// window.alert("이미 좋아요를 눌렀습니다.")
// return;
// }
if (like) {
// 안좋아요
setLike(false);
setColor("unLike");
// 좋아요 해제하면 firebase, redux에 like_cnt - 1
dispatch(likeActions.minusLikeFB(post_id, like_cnt, like_list));
} else {
// 좋아요
setLike(true);
setColor("like");
// 좋아요를 누르면 firebase, redux에 like_cnt + 1
dispatch(likeActions.addLikeFB(post_id, like_cnt, like_list));
}
};
return (
<React.Fragment>
<Grid>
<Grid is_flex padding="16px">
<Grid is_flex width="auto">
<Image shape="circle" src={props.user_profile}></Image>
<Text bold>{props.user_info.user_name}</Text>
</Grid>
<Grid is_flex width="auto">
<Text>{props.insert_dt}</Text>
{props.is_me && (
<Button
width="auto"
padding="4px"
margin="4px"
_onClick={() => {
history.push(`/postWrite/${props.id}`);
}}
>
수정
</Button>
)}
</Grid>
</Grid>
<Grid
_onClick={() => {
history.push(`postDetail/${props.id}`);
}}
>
<Grid padding="16px">
<Text>{props.contents}</Text>
</Grid>
<Grid>
<Image shape="rectangle" src={props.image_url}></Image>
</Grid>
</Grid>
<Grid padding="16px" is_flex>
<Text bold>댓글 {props.comment_cnt}개</Text>
<Text bold>좋아요 {props.like_cnt}개</Text>
<Permit>
{/* 좋아요 유무 */}
<Like
{...styles}
onClick={() => {
likeClick(props);
}}
>
♥
</Like>
</Permit>
</Grid>
</Grid>
</React.Fragment>
);
};
Post.defaultProps = {
user_info: {
user_name: "오새봄",
user_profile: "https://i.ytimg.com/vi/Ct1Pp_4FEIY/maxresdefault.jpg",
},
image_url: "https://i.ytimg.com/vi/Ct1Pp_4FEIY/maxresdefault.jpg",
contents: "오새봄이다. 찬양해라",
comment_cnt: 10,
insert_dt: "2021-11-29 19:09:00",
};
const Like = styled.div`
// ***
font-size: 30px;
color: ${(props) => (props.color === "like" ? "pink" : "gray")};
`;
export default Post;
'항해 중 > 5주차 리액트 심화반' 카테고리의 다른 글
5주차 - 프로젝트 호스팅 하기 (0) | 2021.12.04 |
---|---|
추가 - 삭제 기능 (0) | 2021.12.03 |
4주차 - 엔터 키로 입력하기 (1) | 2021.12.03 |
4주차 - 댓글 알림 뱃지 만들기 (0) | 2021.12.03 |
4주차 - 댓글 작성하기 (0) | 2021.12.02 |
댓글