TIL React_02 (Component와 Props)

백광호·2021년 2월 10일
0

TIL

목록 보기
48/55

Component와 Props

개념적으로 컴포넌트는 JavaScript 함수와 유사하다.
props라고 하는 임의의 입력을 받은 후, 어떻게 표시되는지 기술하는
리액트 엘리먼트를 반환한다.

컴포넌트를 정의하는 가장 간단한 방법은 JavaScript 함수를 작성하는 것이다.

function Welcome(props) {
  return <h1> Hello, {props.name}</h1>;
}

컴포넌트의 이름은 항상 대문자로 시작한다.
이 함수는 어떤 데이터를 가진 하나의 props 객체 인자를 받은 후
리액트 엘리먼트를 반환하므로 유효한 리액트 컴포넌트이다.

이런 컴포넌트는 JavaScript 함수이기 때문에 말 그대로 함수 컴포넌트이다.

이런 방법 외에도 class를 이용해 컴포넌트를 정의할 수 있다.

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>
  }
}

위 두가지 컴포넌트는 동일한 컴포넌트이다.

여기서 중요한 것은 리액트는 자동으로 클래스 컴포넌트의 render() 메소드를 실행한다.

class를 이용해 컴포넌트를 만들면 몇가지 추가 기능을 이용할 수 있다.
이에 대해서는 state를 다룰때 자세히 알아보도록 하겠다.

컴포넌트 렌더링

리액트 엘리먼트는 사용자 정의 컴포넌트로도 나타낼 수 있다.

const element = <Welcome name="Sara" />

리액트가 사용자 정의 컴포넌트로 작성한 엘리먼트를 발견하면
JSX 어트리뷰트와 자식을 해당 컴포넌트에 단일 객체로 전달한다.
이 객체를 props라고 한다.

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

위 코드를 렌더링하면 다음과같은 일들이 일어난다.

  1. <Welcome name="Sara" /> 엘리먼트로 ReactDOM.render()를 호출한다.
  2. 리액트는 {name: 'Sara'}를 props로 하여 Welcome 컴포넌트를 호출한다.
  3. Welcome 컴포넌트는 결과적으로 <h1>Hello, Sara</h1> 엘리먼트를 반환한다.
  4. 리액트 DOM은 <h1>Hello, Sara</h1> 엘리먼트와 일치하도록 DOM을 효율적으로 업데이트한다.

Fragments

하나의 컴포넌트에 여러개의 엘리먼트를 리턴하는 방법이다.

class Table extends React.Component {
  render() {
    return (
      <table>
        <tr>
          <Columns />
        </tr>
      </table>
    );
  }
}

class Columns extends React.Component {
  render() {
    return (
      <div>
        <td>Hello</td>
        <td>World</td>
      </div>
    );
  }
}

이런 컴포넌트가 있다고 가정해보자
내가 원하는 출력결과는 아래와 같지만

<table>
  <tr>
    <td>Hello</td>
    <td>World</td>
  </tr>
</table>

실제로는 아래처럼 출력될 것이다.

<table>
  <tr>
    <div>
      <td>Hello</td>
      <td>World</td>
    </div>
  </tr>
</table>

이렇게 된 이유는 다음과 같다.
컴포넌트에서는 반드시 하나의 최상위 엘리먼트만 반환해야한다.

이런 이유 때문에 Columns에서도 div태그를 이용해 자식들을 감싸줬는데
Fragements는 이런 문제들을 해결해주기 위해 생겼다.

class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );
  }
}

Columns에서 이런식으로 작성 한다면
마치 여러개의 엘리먼트를 출력하는 것과 같다.

이렇게 작성했을 때의 출력 결과는 내가 원하는 출력 결과가 나올 것이다.

<table>
  <tr>
    <td>Hello</td>
    <td>World</td>
  </tr>
</table>

Fragment를 좀 더 간결하게 쓰는 방법이 있다.

class Columns extends React.Component {
  render() {
    return (
      <>
        <td>Hello</td>
        <td>World</td>
      </>
    );
  }
}

다만, fragment에 key가 있는경우 문법으로 명시적으로 선언해줘야 한다.

function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        // React는 `key`가 없으면 key warning을 발생합니다.
        <React.Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </React.Fragment>
      ))}
    </dl>
  );
}

조건부 렌더링

리액트에서 조건부 렌더링은 JavaScript에서 조건문을 사용하는 것과 같다.

아래 두가지 컴포넌트가 있다고 가정해 보자

function UserGreeting(props) {
  return <h1>Welcome back!</h1>;
}

function GuestGreeting(props) {
  return <h1>Please sign up.</h1>;
}

위 컴포넌트가 조건에 따라 렌더링 되게 하려면
아래와 같이 작성하면 된다.

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

ReactDOM.render(
  // Try changing to isLoggedIn={true}:
  <Greeting isLoggedIn={false} />,
  document.getElementById('root')
);

만약 if-else구문을 사용해야 된다면 삼항 연산자를 사용해야된다.

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
    </div>
  );
}

가독성은 떨어지지만 더 큰 표현식에도 사용할 수 있다.

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      {isLoggedIn
        ? <LogoutButton onClick={this.handleLogoutClick} />
        : <LoginButton onClick={this.handleLoginClick} />
      }
    </div>
  );
}

아래의 예제는 잘못 만들어진 조건부 렌더링이다.

const tweet = <Tweet writer="Pangho">
  {
    if (nowLearning) {
      return 'React'
    } else {
      return 'Videogame'
    }
  }is Awesome!
</Tweet>

아래처럼 작성하면 고칠 수 있다.

function React(props) {
  return <div>React</div>
}

function Videogame(porps) {
  return <div>Videogame</div>
}

function Tweet(props) {
  const user = props.writer;
  return (
    <div> {user}: {nowLearning ? <React /> : <Videogame />}is Awesome!</div>
  )
}

이런식으로 작성하면 조건부 렌더링이 될 것이다.

Props

함수 컴포넌트나 클래스 컴포넌트 모두 컴포넌트의 자체 props를 수정해서는 안된다.
입력값을 바꾸지 않고 항상 동일한 입력값에 대해 동일한 결과를 반환하는 함수를
순수 함수(Pure Function)라고 한다.

리액트는 이런 순수함수 규칙을 엄격히 지켜야 하기 때문에
리액트 컴포넌트는 자신의 props를 다룰 때에는 반드시 순수 함수처럼 동작해야 한다.
(입력값을 바꾸지 않고 동일한 입력값에 대해 동일한 결과를 반환한다.)

리스트와 key

컴포넌트에서 리스트를 렌더링 하는 방법에 대해 알아보자
우선 JavaScript에서는 map()함수를 이용해
리스트를 변환하는 방법을 우리는 알고 있다.

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);

위 예제는 JavaScript에서 사용하는 map() 예제이다.
numbers 배열의 값을 두배로 만든 후 새 배열을 doubled 변수에 할당하고
로그를 확인하는 코드이다.

리액트에서 배열을 엘리먼트 리스트로 만드는 방식도 이와 거의 동일하다.
엘리먼트 모음을 만들고 중괄호를 이용해 JSX에 포함 시킬 수 있다.

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li>{number}</li>
)

배열의 map() 메소드와 같이 콜백함수에 인자로 들어가는 값이 같다.

  • 현재 처리되고 있는 요소 (currentValue)
  • 현재 처리되고 있는 요소의 index 값 (index)
  • 메소드가 불려진 배열 (array)

이렇게 하면 배열의 개수만큼 엘리먼트를 만들 수 있다.
이렇게 만든 엘리먼트는 listItems에 저장되어 있으니
이걸 DOM에 렌더링하면 된다.

ReactDOM.render(
  <ul>{listItems}</ul>
  document.getElementById('root')
);

위 예제는 1부터 5까지의 숫자로 이루어진 리스트를 보여준다.

이걸 컴포넌트로 만들면 아래처럼 만들 수 있다.

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li>{number}</li>
  );
  return (
  <ul>{listItems}</ul>
  )
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />
  document.getElementById('root')
);

문제는 여기서 코드를 실행하면 리스트의 각 항목에 key를 넣어야 한다는 경고가 표시된다.

key는 엘리먼트 리스트를 만들때 포함해야 하는 특수한 문자열 속성이다.
해당 경고를 없애려면 우선 listItems 안에서 key를 할당해줘야된다.

  const listItems = numbers.map((number) =>
    <li key={number.toString()}>{number}</li>
  );

key는 리액트가 어떤 항목을 변경, 추가, 삭제할지 식별하는 것을 도와준다.
엘리먼트에 안정적인 고유성을 부여해 배열 내부의 엘리먼트에 지정한다.

key를 지정하는 가장 좋은 방법은 해당 항목을 고유식별할 수 있는 문자열로 지정하는 것이다.
대부분의 경우 데이터의 id를 key로 사용한다.
만약 데이터에 id가 없는 경우 최후의 수단으로써 인덱스를 key로 사용한다.

DOM 노드의 자식들을 재귀적으로 처리할 때 리액트는 기본적으로
동시에 두 리스트를 순회하고 차이점이 있으면 변경한다.
만약 키가 없다면 두 리스트가 동일하기 때문에 차이점을 발견하지 못해
원하는대로 리스트를 변경할 수 없을 것이다.

반대로 키가 있다면 두 리스트의 키값을 비교해 차이점을 찾아 변경할 것이다.
이런 이유 때문에 꼭 key가 필요하다. key가 꼭 필요한 이유

profile
안녕하세요

0개의 댓글