💻/[과제]
[과제] React Twittler State & Props
Mia_
2022. 12. 4. 19:47
File Structure
/
├── /React Twittler State Props
│ ├── README.md
│ ├── /public # create-react-app이 만들어낸 파일, yarn/npm start로 실행할 시에 쓰입니다
│ └── /src # React 컴포넌트가 들어가는 폴더
│ ├── static # dummyData가 들어가는 폴더
│ │ └── dummyData.js
│ ├── Pages # 페이지를 표시하는 컴포넌트가 들어가는 폴더
│ │ ├── About.css
│ │ ├── About.js
│ │ ├── Mypage.css
│ │ ├── Mypage.js
│ │ ├── Tweets.css
│ │ └── Tweets.js
│ ├── Components # 단일 컴포넌트가 들어가는 폴더
│ │ ├── Tweet.css
│ │ └── Tweet.js
│ ├── App.css
│ ├── App.js
│ ├── Footer.js
│ ├── index.js
│ └── Sidebar.js
├ package.json
└ .gitignore
App.js
import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// TODO : React Router DOM을 설치 후, import 구문을 이용하여 BrowserRouter, Routes, Route 컴포넌트를 불러옵니다. - done!
import Sidebar from './Sidebar';
import Tweets from './Pages/Tweets';
// TODO : MyPage, About 컴포넌트를 import 합니다. -done!
import MyPage from './Pages/MyPage';
import About from './Pages/About';
import './App.css';
import './global-style.css';
const App = (props) => {
return (
<BrowserRouter>
<div className="App">
<main>
<Sidebar /> {/*사이드 바는 모든 페이지에 있으니까*/}
<section className="features">
{/* TODO : 유어클래스를 참고해서, 테스트 케이스를 통과하세요. - done!
TODO : React Router DOM 설치 후 BrowserRouter, Routes, Route의 주석을 해제하고
Routes, Route 컴포넌트를 적절하게 작성합니다. - done!*/}
<Routes>
<Route path='/' element={<Tweets />} /> {/* 요소에 경로 지정*/}
<Route path='/about' element={<About />} />
<Route path='/mypage' element={<MyPage />} />
</Routes>
</section>
</main>
</div>
</BrowserRouter>
);
};
// ! 아래 코드는 수정하지 않습니다.
export default App;
Footer.js
import React from 'react';
const Footer = () => {
return <footer><div></div></footer>;
};
// TODO : Footer 함수 컴포넌트를 작성합니다. 시멘틱 요소 footer가 포함되어야 합니다. - done!
export default Footer;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import dummyTweets from './static/dummyData';
ReactDOM.render(
<App dummyTweets={dummyTweets} />,
document.getElementById('root')
);
//원래 ReactDOM 렌더 단계에서 <BrowserRouter>를 넣어서 뿌리는게 국룰임
Sidebar.js
import React from 'react';
// TODO : React Router DOM의 Link 컴포넌트를 import 합니다. - done!
import { Link } from 'react-router-dom';
const Sidebar = () => {
return (
<section className="sidebar">
{/* TODO : Link 컴포넌트를 작성하고, to 속성을 이용하여 경로(path)를 연결합니다. - done! */}
<Link to="/"><i className="far fa-comment-dots"></i></Link>
<Link to="/about"><i className="far fa-question-circle"></i></Link>
<Link to="/mypage"><i className="far fa-user"></i></Link>
</section>
);
};
export default Sidebar;
Components
Tweet.js
: 단일 컴포넌트가 들어있는 폴더; Components
import React from 'react';
import './Tweet.css';
const Tweet = ({ tweet }) => {
const parsedDate = new Date(tweet.createdAt).toLocaleDateString('ko-kr');
return (
<li className="tweet" id={tweet.id}>
<div className="tweet__profile">
<img src={tweet.picture} />
</div>
<div className="tweet__content">
<div className="tweet__userInfo">
<div className="tweet__userInfo--wrapper">
{/* TODO : 유져 이름이 있어야 합니다. - done! */}
<span className='tweet__username'>{tweet.username}</span>
{/* TODO : 트윗 생성 일자가 있어야 합니다. parsedDate를 이용하세요. - done! */}
<span className='tweet__createdAt'>{parsedDate}</span>
</div>
</div>
{/* TODO : 트윗 메세지가 있어야 합니다. - done! */}
<div className='tweet__message'>{tweet.content}</div>
</div>
</li>
);
};
export default Tweet;
- props를 이용하여 정의된 값과 속성을 전달
- 함수에 인자를 전달하듯 React 컴포넌트에 props를 전달하면 되고, 이 props가 필요한 모든 데이터를 가지고 오게 됨
Pages
Tweets.js
: 페이지를 표시하는 컴포넌트가 들어가는 폴더;Pages
- [state 저장 변수, state 갱신 함수] = useState(상태 초기값);
- 서로 다른 주소값을 가져야 다른 자료형으로 인지 ---> State가 다를 때 리렌더링을 함
- 참조 자료형이 다른 자료값을 가지고 있어야 리렌더링이 된다는 것이 포인트!
- 1. 깊은 복사 (내용 통으로 다 복사하는 것, 주소만 복사(얕은 복사)하는게 아니라 값을 아예 다 복사)
- 2. Spread 문법 (깊은 복사 개념)
- 얕은 복사는 같은 주소값을 가지고 있기 때문에 감지하지 못함
1. 새로운 내용이 담긴 것이 tweet
2. 빈 배열에 tweet을 넣음 그리고 기존 전체 트윗을 복사해 옴
3. 빈 배열을 선언해서 다른 자료형으로 인지
// TODO : useState를 react로 부터 import 합니다. - done!
import React, { useState } from 'react';
import Footer from '../Footer';
import Tweet from '../Components/Tweet';
import './Tweets.css';
import dummyTweets from '../static/dummyData';
const Tweets = () => {
const getRandomNumber = (min, max) => {
return parseInt(Math.random() * (Number(max) - Number(min) + 2));
};
// TODO : 새로 트윗을 작성하고 전송할 수 있게 useState를 적절히 활용하세요. - done!
// [state 저장 변수, state 갱신 함수] = useState(상태 초기값);
const [name, setName] = useState("parkhacker");
const [contents, setContents] = useState("");
const [tweets, setTweets] = useState(dummyTweets);
// TODO : Tweet button 엘리먼트 클릭시 작동하는 함수를 완성. - done!
// 트윗 전송이 가능하게 작성해야 함
const handleButtonClick = (event) => {
const tweet = {
id: dummyTweets.length+1,
username: name,
picture: `https://randomuser.me/api/portraits/women/${getRandomNumber(
1,
98
)}.jpg`,
content: contents,
createdAt: new Date().toLocaleDateString('ko-kr'),
updatedAt: new Date().toLocaleDateString('ko-kr'),
};
// 서로 다른 주소값을 가져서야 다른 자료형으로 인지해서
// State가 다를 때 리렌더링을 함
// 참조 자료형이 다른 자료값을 가지고 있어야 리렌더링이 된다는 것이 포인트
// ! 1. 깊은 복사 방법 (내용 통으로 다 복사하는거, 주소만 복사하는게 아니라 값을 아예 다 복사)
// const tweetList = tweets.slice(0);
// tweetList.unshift(tweet);
// setTweets(tweetList);
// ! 2. Spread 문법 방법(깊은 복사 개념)
// 같은 주소값을 가지고 있기 때문에 감지 하지 못함
//1. 새로운 내용이 담긴 것이 트윗
//2. 빈배열에 tweet을 넣음 그리고 기존 전체 트윗을 복사해 옴
//3. 빈배을을 선언해서 다른 자료형으로 인지
// tweets의 초깃값이 dummyTweets로 되어 있음
setTweets([tweet, ...tweets])
setTweets([tweet, ...tweets])
};
const handleChangeUser = (event) => {
// TODO : Tweet input 엘리먼트에 입력 시 작동하는 함수를 완성 - done!
setName(event.target.value);
};
const handleChangeMsg = (event) => {
// TODO : Tweet textarea 엘리먼트에 입력 시 작동하는 함수를 완성 - done!
setContents(event.target.value);
};
return (
<React.Fragment>
<div className="tweetForm__container">
<div className="tweetForm__wrapper">
<div className="tweetForm__profile">
<img src="https://randomuser.me/api/portraits/men/98.jpg" />
</div>
<div className="tweetForm__inputContainer">
<div className="tweetForm__inputWrapper">
<div className="tweetForm__input">
<input
type="text"
value={name}
placeholder="your username here.."
className="tweetForm__input--username"
onChange={handleChangeUser}
></input>
{/*TODO : 트윗을 작성할 수 있는 textarea 엘리먼트를 작성하세요.*/}
<textarea
placeholder="your tweet here.."
className='tweetForm__input--message'
value={contents}
onChange={handleChangeMsg}
></textarea>
</div>
<div className="tweetForm__count" role="status">
<span className="tweetForm__count__text">
{/* TODO : 트윗 총 개수를 보여줄 수 있는 Counter를 작성하기 - done!*/}
{`total: ${tweets.length}`}
</span>
</div>
</div>
<div className="tweetForm__submit">
<div className="tweetForm__submitIcon"></div>
{/* TODO : 작성한 트윗을 전송할 수 있는 button 엘리먼트를 작성하기 */}
<button
className ="tweetForm__submitButton"
onClick={handleButtonClick}
>Tweet</button>
</div>
</div>
</div>
</div>
<div className="tweet__selectUser"></div>
<ul className="tweets">
{/* TODO : 하나의 트윗이 아니라, 주어진 트윗 목록(dummyTweets) 갯수에 맞게 보여주기*/}
{ /*컴포넌트로 추출하면 <li> 요소 안이 아니라 컴포넌트에 써야 함 */ }
{ tweets.map((el) => {
return (
<Tweet key={el.id} tweet={el} />
);
})}
</ul>
<Footer />
</React.Fragment>
);
};
export default Tweets;
Pages
About.js
import React from 'react';
import Footer from '../Footer';
import './About.css';
const About = (props) => {
return (
<section className="aboutTwittler">
<div className="aboutTwittler__container">
<div className="aboutTwittler__wrapper">
<div className="aboutTwittler__detail">
<p className="aboutTwittler__detailName">React Twittler Info</p>
</div>
</div>
</div>
<div className="aboutTwittler__content">
<i className="fas fa-users"></i>
<p>나만의 Twittler 소개페이지를 꾸며보세요.</p>
</div>
<Footer />
</section>
);
};
export default About;
Pages
MyPage.js
import React from 'react';
import Footer from '../Footer';
import Tweet from '../Components/Tweet';
import './MyPage.css';
import dummyTweets from '../static/dummyData';
const MyPage = () => {
// TODO : 주어진 트윗 목록(dummyTweets)중 현재 유져인 parkhacker의 트윗만 보여줘야 합니다.
const filteredTweets = dummyTweets.filter((tweet) => {
return tweet.username === 'parkhacker'
});
return (
<section className="myInfo">
<div className="myInfo__container">
<div className="myInfo__wrapper">
<div className="myInfo__profile">
<img src={filteredTweets[0].picture} />
</div>
<div className="myInfo__detail">
<p className="myInfo__detailName">
{filteredTweets[0].username} Profile
</p>
<p>28 팔로워 100 팔로잉</p>
</div>
</div>
</div>
<ul className="tweets__mypage">
<Tweet tweet={filteredTweets[0]}/>
{/* TODO : 주어진 트윗 목록(dummyTweets)중 현재 유져인
parkhacker의 트윗만 보여줘야 합니다. */}
</ul>
<Footer />
</section>
);
};
export default MyPage;