• 부모 컴포넌트로부터 자식 컴포넌트에 데이터를 보내는 방법

왜 써야하는데요?

  • 버튼 컴포넌트의 스타일을 인라인스타일로 지정해보자.
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에 표시된다.

image

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 컴포넌트를 memoMemorizedBtn 컴포넌트를 const로 정의하였다.
  • 기존 Btn 컴포넌트를 render하던 것을 MemorizedBtn 컴포넌트를 render하도록 해보자.
image
  • 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으로 지정되면 에러가 발생한다.

결과

image

주의사항

  • 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>