- 부모 컴포넌트로부터 자식 컴포넌트에 데이터를 보내는 방법
왜 써야하는데요?
- 버튼 컴포넌트의 스타일을 인라인스타일로 지정해보자.
function SaveBtn() {
return (
<button
style={% raw %}{{{% endraw %}
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
{% raw %}}}{% endraw %}
>
Save Changes
</button>
);
}- style은 여러 요소들을 조작해야하므로 코드 양이 많다.
- 다른 버튼을 또 만들려면 같은 스타일이어도 또 코드를 반복해야한다.
- 코드의 양이 많아지고 유지보수가 불편해진다.
- 이를 Props를 이용해 부모 컴포넌트로부터 Configuration(설정)을 받아온다면 편하게 처리할 수 있다.
function Btn(props) {
return (
<button
style={% raw %}{{{% endraw %}
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
{% raw %}}}{% endraw %}
>
{props.banana}
</button>
);
}
function App() {
return (
<div>
<Btn banana="Save changes" />
<Btn banana="Continue" />
</div>
);
}- 어떠한 단어로 지정해도 된다는 점을 강조하기 위해 property 이름을 'banana'로 지정
- 컴포넌트의 첫번째이자 유일한 argument는 props이다.
- 이 props는 부모 컴포넌트에서 호출될 때 태그 내에 정의된 property대로 적용
- 실제로는 함수 argument에 object 형식으로 전달하는 것과 같다.
- property명은 key, property value는 value로 key:value 형식으로 props object에 저장 후 전달
props.~~~ 방식으로 작성하는 게 너무 긴 거 같은데요?
- 위와 같은 이유로 실제로는 props 자체만으로 많이 쓰지 않는다.
- object 내의 key를 바로 가져오는 shortcut 방식으로 주로 사용
function Btn({ banana }) {
return (
<button
style={% raw %}{{{% endraw %}
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
{% raw %}}}{% endraw %}
>
{banana}
</button>
);
}- props는 object이기 때문에 argument 자리에 위와 같이 사용 가능
- props object의 banana key를 바로 사용하는 것
- React가 아닌 JS ES6의 문법
여러 개의 key를 사용하고 싶은데요?
아래와 같이 사용하면 된다.
function Btn({ banana, big }) {
return (
<button
style={% raw %}{{{% endraw %}
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
fontSize: big ? 18 : 16,
{% raw %}}}{% endraw %}
>
{banana}
</button>
);
}
function App() {
return (
<div>
<Btn banana="Save changes" big={true} />
<Btn banana="Continue" big={false} />
</div>
);
}function Btn({ text, fontSize = 16 }) {
return (
<button
.
.- 위와 같이 argument에 값을 default로 지정 가능
- props가 별도로 지정되지 않으면 default 값 적용
- React가 아닌 JavaScript 문법
- Props에 함수를 전달할 수도 있다.
function App() {
const [value, setValue] = React.useState("Save Changes");
const changeValue = () => setValue("Revert Changes");
return (
<div>
<Btn text={value} changeValue={changeValue} />
<Btn text="Continue" />
</div>
);
}- changeValue라는 key와 changeValue라는 함수가 value로 이어졌다.
- 이 역시 마찬가지로 props object에 전달된다.
컴포넌트의 EventListenr가 아닌 prop으로 전달된다.
-
EventListenr in HTML tag : EventListener
-
EventListenr in Custom Component : prop
-
Component 내부의 button tag에 prop의 function을 EventListener로 넣어주어야 한다!!
그렇게까지 중요한 개념은 아니다. 다만 이런 feature가 React에 있음을 이해하자.
Btn 컴포넌트에 다음과 같이 console에 출력하는 코드를 넣어보자.
function Btn({ text, changeValue }) {
console.log(text, "was rendered");
.
.
.이후 Save Change 버튼을 클릭하면 아래와 같이 console에 표시된다.
Save Change는 Revert Changes로 바뀌니까 그렇다 치는데, Continue는 왜 또 생성되죠?
- 그렇다. Continue 버튼까지 rerender되는 것은 낭비이다.
- 부모 컴포넌트에 state에 변화가 있다면 부모 컴포넌트 전체가 rerender되기 때문에 느려질 수 있다.
- 이 컴포넌트가 다시 그려지는 것을 원치 않는다고 React에 알려야 한다.
- 이 경우 기억(memorize)하라는 의미의 React Memo가 사용된다.
const MemorizedBtn = React.memo(Btn);
function App() {
const [value, setValue] = React.useState("Save Changes");
const changeValue = () => setValue("Revert Changes");
return (
<div>
<MemorizedBtn text={value} changeValue={changeValue} />
<MemorizedBtn text="Continue" />
</div>
);
}- Btn 컴포넌트를 memo한 MemorizedBtn 컴포넌트를 const로 정의하였다.
- 기존 Btn 컴포넌트를 render하던 것을 MemorizedBtn 컴포넌트를 render하도록 해보자.
- console 결과 Continue 버튼이 rerender되지 않았다.
- 해당 컴포넌트가 변경된다면 해당 컴포넌트만 rerender되게 하는 기능
우리는 Btn에 한 가지 더 configuration이 가능하다.
// 정상
<Btn text="Save Changes" fontSize={18} />
// 실수
<Btn text={18} fontSize="Save Changes" />- props를 잘못 지정하여 우리가 원하는 컴포넌트를 받을 수는 없는 경우이다.
- 하지만 syntax적으로는 문제가 없기 때문에 console에서 에러 메시지를 받을 수 없다.
- React가 우리에게 "이것이 잘못되었다" 라는 메시지를 보내주면 좋겠다.
- Prop Type을 통해 이를 해결할 수 있다.
Prop Type : 어떤 type의 prop을 받고 있는지 체크해준다.
CDN 링크
<script src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>사용법
Btn.propTypes = {
text: PropTypes.string,
fontSize: PropTypes.number,
};- text는 string 형식, fontSize는 number 형식이기를 원한다.
- 만약에 이에 해당하지 않는 형식이 prop으로 지정되면 에러가 발생한다.
결과
주의사항
- react CDN이
<https://unpkg.com/react@17.0.2/umd/react.production.min.js>인 경우 에러 메시지가 미발생 <https://unpkg.com/react@17.0.2/umd/react.development.js>, development용 CDN으로 바꿔주면 에러 메시지를 확인 가능- React Memo와 함께 사용하면 미반응
참고자료
ReactJS 공식문서 - Typechecking with Proptypes
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js "></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.17.8/babel.min.js"></script>
<script type="text/babel">
function Btn({ text, fontSize = 16 }) {
console.log(text, "was rendered");
return (
<button
style={% raw %}{{{% endraw %}
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
fontSize, // fontSize: fontSize 처럼 속성명과 속성값이 같은 경우 하나만 사용 가능
{% raw %}}}{% endraw %}
>
{text}
</button>
);
}
Btn.propTypes = {
text: PropTypes.string,
fontSize: PropTypes.number,
};
function App() {
const [value, setValue] = React.useState("Save Changes");
const changeValue = () => setValue("Revert Changes");
return (
<div>
<Btn text="Save Changes" fontSize={18} />
<Btn text={18} fontSize="Save Changes" />
</div>
);
}
const root = document.getElementById("root");
ReactDOM.render(<App />, root);
</script>
</html>