React Component에서 다루는 데이터로 두가지가 있다. 바로 Props 와 State 다.
Props 는 부모 컴포넌트가 자식 컴포넌트에게 주는 값이다. 자식 컴포넌트에서는 Props 를 받아오기만하고,
받아온 Props 를 직접 수정 할 수 는 없다. 이는 한 컴포넌트의 속성(Properties)과 같음을 의미한다.
반면에 State 는 컴포넌트 내부에서 선언하며 내부에서 값을 변경 할 수 있다.
State는 한 컴포넌트 안에서 유동적인 데이터를 다룰 때 사용되며, State는 한 컴포넌트의 상태(State)를 나타냅니다.
<div id="name" class="label" onclick="alert('Hello World!');">
Hello world!
</div>
위의 코드는 HTML의 div 태그에 id와 class 속성을 설정하고 onclick 속성에 직접 자바스크립트의 alert 코드를 사용하였다. 우리는 이미 HTML의 속성이라는 개념을 사용 해 왔었다. React에서는 이 속성 개념에 데이터를 전달한다는 개념을 추가 확장한 것입니다.
const Text = ({text}) => {
return <div>{text}</div>
}
const App = () => {
return <Text text='Hello world!'/>
}
위와 같이 부모 컴포넌트(App)에서 자식 컴포넌트(Text)에 속성(Props)을 이용하여 Hello world!라는 문자열 데이터를 전달하는 것을 확인할 수 있다.
import React, { useState } from 'react';
const Text = ({text}) => {
return <div>{text}</div>
}
const App = () => {
const [count, setCount] = useState(0);
return <div>
<Text text={count} />
<div onClick={() => setCount(count + 1)}>+</div>
</div>
}
React의 함수 컴포넌트에서는 State를 사용하기 위해 useState라는 훅(Hook)을 사용해야한다. useState는 State 변수의 초기값을 매개변수로 전달 하여 호출하며, 결과값으로는 배열을 반환하게 된다. 반환된 배열에서는 useState 함수를 호출할 때 설정한 초기값이 할당된 변수와 해당 변수를 수정하기 위한 Set 함수가 포함되어 있다.
const 배열 = useState (데이터 초기값);
배열[0]: 데이터 초기값이 들어간 변수
배열[1]: 데이터를 수정할 수 있는 Set 함수
보통은 반환된 결과값을 자바스크립트의 구조 분해 할당(Destructuring assignment)을 통해 변수와 Set 함수를 할당하여 사용하게 된다.
const [변수명, Set함수명] = useState (데이터 초기값);
useState를 사용하여 할당받은 변수는 불변값(Immutable)이다. 따라서 해당 값은 직접 수정이 불가능하며 해당 값을 변경하기 위해서는 반드시 Set 함수를 사용해야 한다.
const App = () => {
const [count, setCount] = useState(0);
return <div>
<Text text={count} />
<div onClick={() => setCount(count + 1)}>+</div>
</div>
}
여기까지가 기본적인 State & Props 사용 방법이고 Twittler과제를 진행하면서 만든 코드에 수도코드를 적으며 마치겠다.
import React from "react";
// TODO : React Router DOM의 Link 컴포넌트를 import 합니다.
import { Link } from "react-router-dom"; // 링크를 import 해줘야 한다
const Sidebar = () => {
return (
<section className="sidebar">
{/* TODO : Link 컴포넌트를 작성하고, to 속성을 이용하여 경로(path)를 연결합니다. */}
<Link to="/"> //Link to 는 Route Path를 쓸때와 같게 쓰면 된다.
<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;
import React from "react";
const Footer = () => {
return <Footer></Footer>;
};
// TODO : Footer 함수 컴포넌트를 작성합니다. 시멘틱 요소 footer가 포함되어야 합니다.
export default Footer;
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 : 유져 이름이 있어야 합니다. */}
{/* TODO : 트윗 생성 일자가 있어야 합니다. parsedDate를 이용하세요. */}
<span className="tweet__username">{tweet.username}</span> ///트윗의 유저 네임
<span className="tweet__createdAt">{tweet.createdAt}</span> /// 트윗의 생성 일자
</div>
</div>
<div className="tweet__message">{tweet.content}</div> ///트윗박스에 보여질 메시지
</div>
</li>
);
};
export default Tweet;
import React from "react";
import Footer from "../Footer";
import Tweet from "../Components/Tweet";
import "./MyPage.css";
import dummyTweets from "../static/dummyData";
const MyPage = () => {
const filteredTweets = dummyTweets.fillter((tweet) => {
return tweet.username == "parkhacker"; // 필터를 사용하여 유저네임이 parkhacker 만 보여주게끔
});
// TODO : 주어진 트윗 목록(dummyTweets)중 현재 유져인 parkhacker의 트윗만 보여줘야 합니다.
return (
<section className="myInfo">
<div className="myInfo__container">
<div className="myInfo__wrapper">
<div className="myInfo__profile">
<img src={filteredTweets[1].picture} />
</div>
<div className="myInfo__detail">
<p className="myInfo__detailName">
{filteredTweets[1].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;
// TODO : useState를 react로 부터 import 합니다.
import React /* TODO */ from "react";
import Footer from "../Footer";
import Tweet from "../Components/Tweet";
import "./Tweets.css";
import dummyTweets from "../static/dummyData";
const Tweets = () => {
// TODO : 새로 트윗을 작성하고 전송할 수 있게 useState를 적절히 활용하세요.
const getRandomNumber = (min, max) => {
return parseInt(Math.random() * (Number(max) - Number(min) + 2));
}; // 더미데이터의 값을 그대로 가져왔다
const [user, setUser] = useState("parkhacker");
const [msg, setMsg] = useState("");
const [tweets, setTweets] = useState(dummyTweets);
const handleButtonClick = (event) => {
const tweet = {
id: dummyTweets.length,
username: user,
picture: `https://randomuser.me/api/portraits/women/${getRandomNumber(
1,
98
)}.jpg`,
content: msg,
createdAt: new Date().toLocaleDateString("ko-kr"),
updatedAt: new Date().toLocaleDateString("ko-kr"),
};
// TODO : Tweet button 엘리먼트 클릭시 작동하는 함수를 완성하세요.
// 트윗 전송이 가능하게 작성해야 합니다.
setTweets([tweet, ...tweets]); // tweet이 tweets 에 갱신되게끔
};
const handleChangeUser = (event) => {
// TODO : Tweet input 엘리먼트에 입력 시 작동하는 함수를 완성하세요.
setUser(event.target.value); // 작동하게끔 함수 설정
};
const handleChangeMsg = (event) => {
// TODO : Tweet textarea 엘리먼트에 입력 시 작동하는 함수를 완성하세요.
setMsg(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"
defaultValue="parkhacker"
value={user}
placeholder="your username here.."
className="tweetForm__input--username"
onChange={handleChangeUser}
></input>
TODO : 트윗을 작성할 수 있는 textarea 엘리먼트를 작성하세요.
<textarea //위에 인풋을 보면서 구상하였다.
defaultValue="" // 기본적으로 보여지는 값은 빈칸
value={msg} // 값으로 들어오는것은 msg
ClassName="tweetForm__input--message"
placeholder="your message here.."
onChange={handleChangeMsg} // 이벤트함수를 걸어주었다.
></textarea>
</div>
<div className="tweetForm__count" role="status">
<span className="tweetForm__count__text">
{/* TODO : 트윗 총 개수를 보여줄 수 있는 Counter를 작성하세요. */}
{"total: " + tweets.length} // tweets의 길이만큼
</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) 갯수에 맞게 보여줘야 합니다. */}
{tweets.map((el) => {
return <Tweet tweet={el} />; // 갯수에 맞게 보여줘야해서 map을 사용하였다.
})}
</ul>
<Footer />
</React.Fragment>
);
};
export default Tweets;