NEXTJS 로 twitter클론 해보기(7. 더미데이터로 post업로드 하기 )
1. 먼저 post reducer을 만들어 볼까요?
post state구성
export const initialState = {
mainPosts: [
{
id: 1, //post id
User: { //post를 upload한 user
id: 1, //user id
nickname: "leejaehoon", // user nickname
},
content: "첫번째 게시글 #해시태그 #익스프레스", //post content
Image: [ //post 이미지
{
src: "https://gimg.gilbut.co.kr/book/BN001985/rn_view_BN001985.jpg",
},
{
src: "https://gimg.gilbut.co.kr/book/BN001985/rn_view_BN001958.jpg",
},
{
src: "https://gimg.gilbut.co.kr/book/BN001985/rn_view_BN001985.jpg",
},
],
Comments: [ //post 댓글
{
User: { //댓글단 user
nickname: "nero",
},
content: "우와 개정판이 나왔군요~",
},
{
User: {
nickname: "hero",
},
content: "얼른 사고싶어용",
},
],
},
],
imagePaths: [], //이미지 경로
postAdded: false, // 포스트 여부
};
post Upload시 action만들기
const ADD_POST = "ADD_POST";
export const addPost = {
type: ADD_POST,
};
post Upload시 Add할 더미데이터 만들기
const dummyPost = {
id: 2,
content: "더미데이터입니다.",
User: {
id: 1,
nickname: "zerocho",
},
Images: [],
Comments: [],
};
addPost dispatch시 리턴값 만들기
const reducer = (state = initialState, action) => {
switch (action.type) {
case ADD_POST:
return {
...state,
mainPosts: [dummyPost, ...state.mainPosts], //앞쪽에 넣어줘야 젤 앞에 배치된다.
postAdded: true,
};
default:
return state;
}
};
2. pages/index.js 구성하기
isLoggedIn이 true일때 즉 로그인 했을때만 PostForm 컴포넌트를 보여줍니다.
mainPosts의 갯수대로 PostCard 컴포넌트를 보여줍니다.
import AppLayout from "../components/AppLayout";
import { useSelector } from "react-redux";
import PostCard from "../components/PostCard";
import PostForm from "../components/PostForm";
const Home = () => {
const { isLoggedIn } = useSelector(state => state.user);
const { mainPosts } = useSelector(state => state.post);
return (
<>
<AppLayout>
{isLoggedIn && <PostForm />}
{mainPosts.map((post) => (
<PostCard key={post.id} post={post} />
))}
</AppLayout>
</>
);
};
export default Home;
PostForm 컴포넌트
onFinish이벤트가 발생했을때 dispatch(addPost)를 해줍니다.
import { Button, Form, Input } from "antd";
import { useCallback, useState, useRef } from "react";
import { useDispatch } from "react-redux";
import { useSelector } from "react-redux";
import { addPost } from "../reducers/post";
const PostForm = () => {
const dispatch = useDispatch();
const imageInput = useRef();
const [text, setTest] = useState("");
const onClickImageUpload = useCallback(() => {
imageInput.current.click();
}, [imageInput.current]);
const onChangeTest = e => {
setTest(e.target.value);
};
const onSubmitBtn = useCallback(() => {
dispatch(addPost);
setTest("");
}, []);
return (
<Form
style={{ margin: "10px 0 20px" }}
encType="multipart/form-data"
onFinish={onSubmitBtn}
>
<Input.TextArea
value={text}
onChange={onChangeTest}
maxLength={140}
placeholder="어떤 신기한 일이 있었나요?"
/>
<div>
<input type="file" multiple hidden ref={imageInput} />
<Button onClick={onClickImageUpload}>이미지 업로드</Button>
<Button type="primary" htmlType="submit" style={{ float: "right" }}>
짹짹
</Button>
</div>
<div>
{imagePaths.map(v => (
<div key={v} style={{ display: "lnline-block" }}>
<img src={v} style={{ width: "200px" }} alt={v} />
<div>
<Button>제거</Button>
</div>
</div>
))}
</div>
</Form>
);
};
export default PostForm;
PostCard 컴포넌트
import { Avatar, Button, Card, Popover } from "antd";
import PropTypes from "prop-types";
import {
RetweetOutlined,
HeartOutlined,
EllipsisOutlined,
MessageOutlined,
HeartTwoTone,
} from "@ant-design/icons";
import { useSelector } from "react-redux";
import PostImages from "./PostImages";
import { useCallback, useState } from "react";
const PostCard = ({ post }) => {
const [liked, setLiked] = useState(false);
const [commentFormOpened, setCommentFormOpened] = useState(false);
const id = useSelector(state => state.user.me?.id); //옵셔널 체이닝 문법
const onToggleLike = useCallback(() => { //liked가 true면 빨간색으로
setLiked(prev => !prev);
}, [liked]);
const onToggleComment = useCallback(() => { //commentFormOpened이 true면 댓글부분 보이게
setCommentFormOpened(prev => !prev);
}, [commentFormOpened]);
return (
<div style={{ marginTop: 20 }}>
<Card
cover={post.Images[0] && <PostImages images={post.Images} />}
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={post.content}
/>
</Card>
{commentFormOpened && <div>댓글 부분</div>}
{/* <CommentForm /> */}
{/* <Comments /> */}
</div>
);
};
PostCard.propTypes = {
post: PropTypes.shape({
id: PropTypes.number,
User: PropTypes.object,
content: PropTypes.string,
createdAt: PropTypes.object,
Comments: PropTypes.arrayOf(PropTypes.object),
Images: PropTypes.arrayOf(PropTypes.object),
}).isRequired,
};
export default PostCard; ****
댓글 구현하기
PostCard 컴포넌트의 일부분
commentFormOpened가 true일때 즉 댓글 버튼을 눌렀을때 댓글이 보이게 해줍니다.
{commentFormOpened && (
<div>
<CommentForm post={post} />
<List
header={`${post.Comments.length}개의 댓글`}
itemLayout="horizontal"
dataSource={post.Comments}
renderItem={item => (
<li>
<Comment
author={item.User.nickname}
avatar={<Avatar>{item.User.nickname[0]}</Avatar>}
content={item.content}
/>
</li>
)}
/>
</div>
)}
CommentForm 컴포넌트
import { Form, Input, Button } from "antd";
import { useCallback } from "react";
import useInput from "../hooks/useInput";
import PropTypes from "prop-types";
import { useSelector } from "react-redux";
const CommentForm = ({ post }) => {
const id = useSelector(state => state.user.me?.id);
const [commentText, onChangeCommentText] = useInput("");
const onSubmitComment = useCallback(() => {
console.log(post.id, commentText);
}, [commentText]);
return (
<Form
onFinish={onSubmitComment}
style={{ position: "relative", margin: 0 }}
>
<Form.Item>
<Input.TextArea
value={commentText}
onChange={onChangeCommentText}
rows={4}
/>
<Button
type="primary"
htmlType="submit"
style={{ position: "absolute", right: 0, bottom: -40 }}
>
댓글 추가
</Button>
</Form.Item>
</Form>
);
};
CommentForm.propTypes = {
post: PropTypes.object.isRequired,
};
export default CommentForm;
PostImages 컴포넌트
import PropTypes from "prop-types";
import { useCallback, useState } from "react";
import { Carousel } from "antd";
const PostImages = ({ images }) => {
const contentStyle = {
maxWidth: "100%",
witdth: "100%",
};
return (
<>
<Carousel style={contentStyle} autoplay>
{images.map(image => (
<div>
<img src={image.src} style={contentStyle} alt={image.src} />
</div>
))}
</Carousel>
</>
);
};
PostImages.propTypes = {
images: PropTypes.arrayOf(PropTypes.object),
};
export default PostImages;
post reducer state에 있는 maiPosts의 content에는 해시태그가 들어가는데 각 해시태그마다 링크를 걸어주려고 합니다.
postData에는 post의 content가 들어가 있습니다.
정규표현식을 통해 #이 들어간 단어들을 분리하여 배열로 만든후 #으로 시작하는 단어들만 Link를 해뒀습니다.
PostCardContent.js
import PropTypes from "prop-types";
import Link from "next/link";
const PostCardContent = ({ postData }) => {
return (
<div>
{postData.split(/(#[^\s#]+)/g).map((str, i) => {
if (str.startsWith("#")) {
return (
<Link key={i} href={`/hashtag/${str.slice(1)}`}>
<a>{str}</a>
</Link>
);
} else {
return str;
}
})}
</div>
);
};
PostCardContent.propTypes = {
postData: PropTypes.string.isRequired,
};
export default PostCardContent;
Author And Source
이 문제에 관하여(NEXTJS 로 twitter클론 해보기(7. 더미데이터로 post업로드 하기 )), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@abc5259/NEXTJS-로-twitter클론-해보기7.-더미데이터로-post업로드-하기저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)