+ 알아야 할 것

props란 무엇인가, 왜 사용해야 하는가

props의 특징 2가지

+ 실습

players란 객체로 구성된 배열 데이터를 최상위 부모 컴포넌트에서 부터 자식 컴포넌트까지 props로 내려주면서 props를 사용해서 화면을 표시한다.

prerequisite

+ 자바스크립트 Array 메서드 map 과 forEach 차이점

// 아래 실행결과를 예측하고 map과 forEach의 차이점을 설명하시오.
var items = ['1', '2', '3', '4', '5'];
var newItems = items.map(item => parseInt(item));
console.log(newItems);
 
var newItems2 = items.forEach(item => parseInt(item));
console.log(newItems2);

props 기본 개념

컴포넌트가 어떤 상태 정보를 가지게 되었을 때 이 상태정보를 다른 컴포넌트와 공유하고 싶을때 props를 통해서 전달하게 된다. 예를 들어 로그인 후 jwt 토큰 정보를 수신했다면 모든 컴포넌트가 이 토큰 정보를 공유해야만 로그인 했는지 않했는지 알 수 있을 것이다. token 정보를 props를 통해서 모든 컴포넌트에 전달해야만 가능한 일이다.

즉, props의 가장 중요한 점은 컴포넌트간의 통신을 가능하게 한다는 점이다.

props의 가장 중요한 개념 두가지는 다음과 같다.

  • props는 위에서 아래로만 흐른다. 부모가 자식한테만 전달 가능하다.
  • props는 read only 이다. props를 수정할 수 있는 주체는 상태정보를 소유한 루트 컴포넌트만 가능하다. 만일 루트 컴포넌트가 해당 상태 정보를 수정하게 되면 바뀐 정보는 props를 통해서 위에서 아래로 흐르기 때문에 모든 컴포넌트에 바뀐 정보가 전달된다. 루트 컴포넌트 이외에는 이 props 를 수정할 수 없다.

read only props

Header 컴포넌트에 속성을 설정하고 props값이 어떻게 전달되고 받는지 살펴본다. Header 컴포넌트에서 title 속성과 totlaPlayers 속성을 설정한다. title은 스트링으로 넘어갈 것이고  만일 totalPlayers를 number 타입으로 넘기고 싶다면  JSX expression으로 설정해야 한다.

주석을 넣는 방법은 JSX express 안에 슬래쉬 별로 들어가 있는데 웹스톰에서는 control + / 를 누르면 자동으로 처리해준다. 

const App = (props) => (
  <div className="container p-3">
    <Header title="My Scoreboard" totalPlayers={11}/>
    
    {/*Players List*/}
    <Player/>
  </div>);

Header 에서는 먼저 console.log 창에서 출력해보면 props가 객체로 넘어온다는것을 확인할 수 있이다. 그러면 JSX expression으로 해당값을 출력한다.

const Header = (props) => {
  console.log(props);
  // props는 read only 이다. 아래와 같이 하면 안된다.
  // props.totalPlayers = 12;
  return (<div className="header d-flex justify-content-between align-items-center p-2">
    <span>Total Score: 0</span>
    <h1>{props.title}</h1>
    <span>players: {props.totalPlayers}</span>
  </div>)
};

만일, props.totalPlayers = 12 와 같이 props를 변경하게 되면 에러가 발생할것이다. props는 read only라는 규칙을 어겼기 때문이다. 그러면 누가 이 값을 변경할 수 있는가? 이 상태를 최초로 갖고 루트 컴포넌트만이 가능하다.

Resuable Components

Player가 한명이 아니라 아래와 같이 여러명이 있을 경우를 고려해보자. Player의 name 과 score props를 App에서 전달하자

const App = (props) => (
  <div className="container p-3">
    <Header title="My Scoreboard" totalPlayers={11}/>

    {/*Players List*/}
    <Player name="LDK" score={50} />
    <Player name="HONG" score={60} />
    <Player name="KIM" score={70} />
    <Player name="PARK" score={80} />
  </div>);

Player 컴포넌트에서 name 을 표시하고 score는 Player 컴포넌트에서 처리하는게 아니라 하위 컴포넌트인 Counter 컴포넌트로 다시 넘겨야 한다.

const Player = (props) => (
  <div className="container">
    <div className='player row align-items-center'>
      <div className="col-9">
        <span>{props.name}</span>
      </div>
      <div className="col-3 counter">
        <Counter score={props.score} />
      </div>
    </div>
  </div>
);

const Counter = (props) => (<div className='d-flex justify-content-between align-items-center'>
  <button className='btn btn-info' > - </button>
  <span>{props.score}</span>
  <button className='btn btn-info'> + </button>
</div>)

Display iterating players

여러개의 player들을 가지고 있는 json Array데이터가 아래와 같이 정의되어있다고 가정하자. 실제로는 서버에서 REST API 형태로 가져오겠지만 여기서는 mockup 데이터라고 생각하자.

만일, angular 라면 ngFor 라는 디렉티브를,  vue 라면 v-for 라는 반복문을 수행하는 별도의 template language를 사용할 것이다. 그러나 리액트는 별도의 template 언어가 없고 순수하게 자바스크립트 언어만을 사용해서 반복문을 구현한다.

const players = [
  {name: 'LDK', score: 30},
  {name: 'HONG', score: 40},
  {name: 'KIM', score: 50},
  {name: 'PARK', score: 60},
];

이 json Array를 top-level 엘리먼트인 App에 props로 입력한 후에 다시 App 컴포넌트에서 props로 받은 후에 player에게 전달한다. props 는 read only 이기 때문에 최상단 엘리먼트가 모든 props를 다 가지고 있다가 그걸 자식에게 넘겨주는 형태가 바람직하다.

const App = (props) => (
  <div className="container p-3">
    <Header title="My Scoreboard" totalPlayers={11}/>

    {/*Players List*/}
    { props.initialPlayers.map(item => <Player name={item.name} score={item.score} />) }
  </div>);

ReactDOM.render(<App initialPlayers={players} />, document.getElementById('root'));

map은 새로운 배열을 리턴한다. JSX expression은 모두 { } 안에 포함되어져야 한다. 실행은 잘되지만 콘솔창을 보면 warning이 발생할것이다. player Array를 특정한 key로 구분해야 한다.

const App = (props) => (
  <div className="container p-3">
    <Header title="My Scoreboard" totalPlayers={11}/>

    {/*Players List*/}
    { props.initialPlayers.map(item => <Player name={item.name} score={item.score} key={item.name} />) }
  </div>);

ReactDOM.render(<App initialPlayers={players} />, document.getElementById('root'));

map으로 리턴한 결과는 배열이고 각 배열은 react element 인데 위와 결과가 똑같지아 않다고 생각할 수도 있다. 아래와 같이

{[
    <Player name="LDK" score={50} />,
    <Player name="HONG" score={60} />,
    <Player name="KIM" score={70} />,
    <Player name="PARK" score={80} />
]}

JSX expression 안에 배열이 들어가 있고 이것을 evaluate 하게 되면 결국 react element 가 4개가 되므로 결과는 동일하다.

+ ToDo

TodoApp 컴포넌트에 initData로 배열을 넣었다. 이 데이터를 Todo까지 내려서 목록 리스트를 구성해보자.

+ quiz