201005_TIL

oh_ji_0·2020년 10월 5일
1

TIL

목록 보기
43/61

Today I learned

  • 리액트 공식문서 학습
  • JSX
  • Component
  • state
  • 이벤트 처리
  • Form
  • 합성, 상속
  • Life Cycle

@@ 오늘은 리액트에 대해서 학습하는 시간을 가졌다. 공식문서를 토대로 함수형 컴포넌트와 클래스 컴포넌트를 배우고, 컴포넌트 내에서 변화하는 값인 state와 부모 컴포넌트가 자식 컴포넌트에게 전달해주는 Props에 대한 개념을 학습했다. 컴포넌트의 생명 주기인, 라이프 싸이클에 대해서 알아보았고, input, textarea등 Form 태그를 리액트 컴포넌트로 작성하는 법을 학습했다. 그리고 배운 개념을 토대로 이 전에 html, css로 구성했던 트위틀러 스프린트를 리액트로 다시 구현하는 시간을 가졌다.

생각보다 레슨은 쭉쭉 나가게 되어서, Lifting State Up 과 단방향 데이터 흐름 등 내일 배울 내용도 미리 예습하는 시간을 가졌는데 자식 컴포넌트가 부모컴포넌트의 상태에 접근해 어떻게 간접적으로 상태를 조작할 수 있는지도 학습해보는 시간을 가졌다.
리액트가 아닌 순수 자바스크립트 코드를 통해 배우도록 레슨 문서가 짜있었는데, 오히려 그 구조를 파악하는게 좀 어렵게 느껴졌다. (그렇기때문에 리액트를 쓰는 것이겠지만). 여튼 요지는 상태를 조작하는 함수를 자식 함수에 넘겨주고, 자식함수는 필요할 때 그 함수를 실행시킬 수 있다는 것이 핵심 요점이었다.

JSX

  • JSX
    • JS 확장 문법. Javascript의 모든 기능이 포함되어 있다.
    • 리액트 엘리먼트를 생성한다.
    • 렌더링 로직이 본질적으로 다른 UI로직과 연결된다.
  • JSX 속성 정의

    • 문자열 리터럴을 정의할 수 있으며 중괄호를 사용해서 자바스크립트 표현식을 삽입할 수 있다.
  • JSX는 주입 공격( XSS ) 을 방지한다.

    • 리액트 돔은 JSX에 삽입된 모든 값을 렌더링 하기 전에 이스케이프한다.
    • 애플리케이션에 명시적으로 작성되지 않은 내용은 주입되지 않는다.
  • 바벨은 JSX를 React.createElement() 호출로 컴파일한다.

  • 엘리먼트 렌더링

    • 리액트로 구현된 애플리케이션은 일반적으로 하나의 루트 돔 노드가 있다

    • 리액트를 기존 앱에 통합하려는 경우 원하는 만큼 많은 수의 독립된 루트 돔 노드가 있을 수 있다.

    • 리액트 엘리먼트를 루트 돔 노드에 렌더링하려면 ReactDOM.render()로 전달하면 된다.

    • 리액트 엘리먼트는 불변 객체다.
      해당 엘리먼트를 생성한 이후에는 자식이나 속성을 변경할 수 없다.

    • UI를 업데이트하는 유일한 방법은 새로운 엘리먼트를 생성하고 이를 ReactDOM.render() 로 전달하는 것이다.

      import React from "react";
      import ReactDOM from "react-dom";
      
      function tick() {
        const element = (
          <div>
            <h1>Hello, world!</h1>
            <h2>It is {new Date().toLocaleTimeString()}.</h2>
          </div>
        );
        ReactDOM.render(element, document.getElementById('root'));
      }
      
      setInterval(tick, 1000);
    • 위 코드는 매초 전체의 ui를 다시 그리도록 엘리먼트를 만들었지만 ReactDOM은 내용이 변경된 텍스트 노드만 업데이트 한다.

  • JSX 콜백 안에서 this의 의미에 대해 주의해야 한다. JavaScript에서 클래스 메서드는 기본적으로 바인딩되어 있지 않다.this.handleClick을 바인딩하지 않고 onClick에 전달하였다면, 함수가 실제 호출될 때 this는 undefined가 된다.
    일반적으로 onClick={this.handleClick}과 같이 뒤에 ()를 사용하지 않고 메서드를 참조할 경우 해당 메서드를 바인딩해야한다.

  • bind 호출하지 않고 해결하는 방법

    1) 퍼블릭 클래스 필드 문법.

    class LoggingButton extends React.Component {
      // 이 문법은 `this`가 handleClick 내에서 바인딩되도록 합니다.
      // 주의: 이 문법은 *실험적인* 문법입니다.
      handleClick = () => {
        console.log('this is:', this);
      }
    
      render() {
        return (
          <button onClick={this.handleClick}>
            Click me
          </button>
        );
      }
    }

    2) 화살표 함수 사용

    class LoggingButton extends React.Component {
      handleClick() {
        console.log('this is:', this);
      }
    
      render() {
        // 이 문법은 `this`가 handleClick 내에서 바인딩되도록 합니다.
        return (
          <button onClick={() => this.handleClick()}>
            Click me
          </button>
        );
      }
    }
    • 이 문법은 로깅벙튼이 렌더링될때마다 다른 콜백이 생성된다. 콜백이 하위 컴포넌트에 props로서 전달된다면 그 컴포넌트들은 추가로 다시 렌더링을 수행할 수도 있다.
    • 성능 문제를 피하고자한다면 생성자 안에서 바인딩 혹은 클래스 필드 문법을 권장

Component

  • 자바스크립트 함수와 유사

  • Props라는 임의의 입력을 받은 후 화면에 어떻게 표시하는지를 기술하는 React 엘리먼트를 반환

  • 함수 컴포넌트 와 클래스 컴포넌트

    • 함수컴포넌트

      function Welcome(props) {
        return <h1>Hello, {props.name}</h1>;
      }
    • 클래스 컴포넌트

      class Welcome extends React.Component {
        render() {
          return <h1>Hello, {this.props.name}</h1>;
        }
      }
    • 각각 다른 추가 기능들이 존재한다.

    • 컴포넌트 렌더링

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

        import React from "react";
        import ReactDOM from "react-dom";
        
        function Welcome(props) {
          return <h1>Hello, {props.name}</h1>;
        }
        
        const element = <Welcome name="Sara" />;
        ReactDOM.render(element, document.getElementById("root"));
    • 컴포넌트 합성

      • 컴포넌트는 자신의 출력에 다른 컴포넌트를 참조할 수 있다. 이는 모든 세부 단계에서 동일한 추상 컴포넌트를 사용할 수 있음을 의미한다.
      • 일반적으로 새 리액트 앱은 최상위에 단일 App 컴포넌트를 갖고 있다. 하지만 기존 앱에 React 를 통합하는 경우엔 Button과 같은 작은 컴포넌트부터 시작해서뷰 계층의 상단으로 올라가면서 점진적으로 작업해야 할 수 있다.
    • 컴포넌트 추출

      • props의 이름은 사용될 컨텍스트가 아닌 컴포넌트 자체의 관점에서 짓는 것을 권장
      • 큰앱에서 작업할 때 재사용 가능한 컴포넌트를 만들어 놓는 것의 효율이 두각을 나타낸다
    • props는 읽기 전용이다.

      • 함수 컴포넌트나 클래스 컴포넌트 모두 컴포넌트의 자체 props를 수정해서는 안된다.

      • 순수함수

        입력값을 바꾸려고 하지않고 항상 동일한 입력값에 대해 동일한 결과를 반환

        • 순수함수가 아닌 예

          function withdraw(account, amount) {
            account.total -= amount;
          }
    • 모든 리액트 컴포넌트는 자신의 props를 다룰 때 순수함수처럼 동작해야 한다.

    • 리액트 컴포넌트는 위 규칙을 위반하지 않고, 사용자 액션, 네트워크 응답 및 요소에 대한 응답으로 시간에 따라 자신의 출력값을 변경할 수 있다.

  • 조건부 렌더링

    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')
    );
  • 엘리먼트 변수

    • 엘리먼트를 저장하기 위해 변수를 사용할 수 있다.

      function LoginButton(props) {
        return (
          <button onClick={props.onClick}>
            Login
          </button>
        );
      }
      
      function LogoutButton(props) {
        return (
          <button onClick={props.onClick}>
            Logout
          </button>
        );
      }
      
      class LoginControl extends React.Component {
        constructor(props) {
          super(props);
          this.handleLoginClick = this.handleLoginClick.bind(this);
          this.handleLogoutClick = this.handleLogoutClick.bind(this);
          this.state = {isLoggedIn: false};
        }
      
        handleLoginClick() {
          this.setState({isLoggedIn: true});
        }
      
        handleLogoutClick() {
          this.setState({isLoggedIn: false});
        }
      
        render() {
          const isLoggedIn = this.state.isLoggedIn;
          let button;
          if (isLoggedIn) {
            button = <LogoutButton onClick={this.handleLogoutClick} />;
          } else {
            button = <LoginButton onClick={this.handleLoginClick} />;
          }
      
          return (
            <div>
              <Greeting isLoggedIn={isLoggedIn} />
              {button}
            </div>
          );
        }
      }
      
      ReactDOM.render(
        <LoginControl />,
        document.getElementById('root')
      );
    • 더 짧은 구문을 사용하고 싶을 때

      function Mailbox(props) {
        const unreadMessages = props.unreadMessages;
        return (
          <div>
            <h1>Hello!</h1>
            {unreadMessages.length > 0 &&
              <h2>
                You have {unreadMessages.length} unread messages.
              </h2>
            }
          </div>
        );
      }
      
      const messages = ['React', 'Re: React', 'Re:Re: React'];
      ReactDOM.render(
        <Mailbox unreadMessages={messages} />,
        document.getElementById('root')
      );
    • JavaScript의 논리 연산자 &&를 사용하면 쉽게 엘리먼트를 조건부로 넣을 수 있다.

      JavaScript에서 true && expression은 항상 expression으로 평가되고 false && expression은 항상 false로 평가. 따라서 && 뒤의 엘리먼트는 조건이 true일때 출력. 조건이 false라면 React는 무시하게 된다.

    • 조건부 연산자 활용

      condition ? true: false

      render() {
        const isLoggedIn = this.state.isLoggedIn;
        return (
          <div>
            {isLoggedIn
              ? <LogoutButton onClick={this.handleLogoutClick} />
              : <LoginButton onClick={this.handleLoginClick} />
            }
          </div>
        );
      }
    • 컴포넌트가 렌더링하는 것을 막기

      다른 컴포넌트에 의해 렌더링될 때 컴포넌트 자체를 숨기고 싶을 때, 렌더링 결과를 출력하는 대신 null을 반환

      function WarningBanner(props) {
        if (!props.warn) {
          return null;
        }
      
        return (
          <div className="warning">
            Warning!
          </div>
        );
      }
      
      class Page extends React.Component {
        constructor(props) {
          super(props);
          this.state = {showWarning: true};
          this.handleToggleClick = this.handleToggleClick.bind(this);
        }
      
        handleToggleClick() {
          this.setState(state => ({
            showWarning: !state.showWarning
          }));
        }
      
        render() {
          return (
            <div>
              <WarningBanner warn={this.state.showWarning} />
              <button onClick={this.handleToggleClick}>
                {this.state.showWarning ? 'Hide' : 'Show'}
              </button>
            </div>
          );
        }
      }
      
      ReactDOM.render(
        <Page />,
        document.getElementById('root')
      );
      • WarningBanner에 warn props를 null값을 주는 경우 해당 컴포넌트가 아예 무시된다.
      • 컴포넌트의 렌더 메서드로부터 null 을 반환하는 것은 생명주기 메서드 호출에 영향을 주지 않는다. componentDidUpdate는 계속해서 호출된다.
    • 리스트와 Key

      • 여러개의 컴포넌트 렌더링

        const numbers = [1, 2, 3, 4, 5];
        const listItems = numbers.map((number) =>
          <li>{number}</li>
        );
        
        ReactDOM.render(
          <ul>{listItems}</ul>,
          document.getElementById('root')
        );
      • 컴포넌트 안에서 리스트 렌더링

        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

        엘리먼트 리스트를 만들때 포함해야하는 특수 문자열 어트리뷰트

        function NumberList(props) {
          const numbers = props.numbers;
          const listItems = numbers.map((number) =>
            <li key={number.toString()}>
              {number}
            </li>
          );
          return (
            <ul>{listItems}</ul>
          );
        }
        
        const numbers = [1, 2, 3, 4, 5];
        ReactDOM.render(
          <NumberList numbers={numbers} />,
          document.getElementById('root')
        );
        • key는 리액트가 어떤 항목을 변경, 추가 또는 삭제할 지 식별하는 것을 돕는다.
        • 엘리먼트에 안정적 고유성을 부여하기 위해 배열 내부의 엘리먼트에 지정해야 한다.
        • Key를 선택하는 가장 좋은 방법은 리스트의 다른 항목들 사이에서 해당 항목을 고유하게 식별할 수 있는 문자열을 사용하는 것(대부분 데이터의 아이디값을 사용한다)
        • 안정적 ID 가 없다면 최후의 수단으로 항목의 인덱스를 키로 사용할 수 있다.
        • Robin Pokorny’s가 작성한 글인 인덱스를 key로 사용할 경우 부정적인 영향에 대한 상세 설명을 참고
      • 키로 컴포넌트 추출

        • 키는 주변 배열의 컨텍스트 에서만 의미가 있다.

          function ListItem(props) {
            // 맞습니다! 여기에는 key를 지정할 필요가 없습니다.
            return <li>{props.value}</li>;
          }
          
          function NumberList(props) {
            const numbers = props.numbers;
            const listItems = numbers.map((number) =>
              // 맞습니다! 배열 안에 key를 지정해야 합니다.
              <ListItem key={number.toString()} value={number} />
            );
            return (
              <ul>
                {listItems}
              </ul>
            );
          }
          
          const numbers = [1, 2, 3, 4, 5];
          ReactDOM.render(
            <NumberList numbers={numbers} />,
            document.getElementById('root')
          );
        • ListItem 컴포넌트를 추출 한 경우 ListItem 안에 있는 <li> 엘리먼트가 아니라 배열의 <ListItem /> 엘리먼트가 key를 가져야 한다.

        • map()함수 내부에 있는 엘리먼트에 key 를 넣어주는 게 좋다.

        • key는 형제사이에서만 고유한 값이면 된다.

        • 리액트에서 key는 힌트를 제공하지만 컴포넌트로 전달하진 않는다.

          function Blog(props) {
            const sidebar = (
              <ul>
                {props.posts.map((post) =>
                  <li key={post.id}>
                    {post.title}
                  </li>
                )}
              </ul>
            );
            const content = props.posts.map((post) =>
              <div key={post.id}>
                <h3>{post.title}</h3>
                <p>{post.content}</p>
              </div>
            );
            return (
              <div>
                {sidebar}
                <hr />
                {content}
              </div>
            );
          }
          
          const posts = [
            {id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
            {id: 2, title: 'Installation', content: 'You can install React from npm.'}
          ];
          ReactDOM.render(
            <Blog posts={posts} />,
            document.getElementById('root')
          );
          const content = posts.map((post) =>
            <Post
              key={post.id}
              id={post.id}
              title={post.title} />
          );
        • 위 예제에서 Post 컴포넌트는 props.id를 읽을 수 있지만 props.key는 읽을 수 없다.

        • JSX 안에도 중괄호를 사용하면 map()함수의 결과를 처리할 수 있다.

state

  • 직접 state를 수정하면 안된다.

  • 직접 수정하게 되면 컴포넌트를 다시 렌더링하지 않는다.

  • 대신 setState()를 사용한다.

  • this.state 를 지정할 수 잇는 유일한 공간은 constructor이다.

  • state 업데이트는 비동기적일 수 도 있다.
    리액트는 성능을 위해 여러 setState()호출을 단일 업데이트로 한꺼번에 처리할 수 있다.
    this.props와 this.state가 비동기적으로 업데이트 될 수 있기 때문에 state를 계산할 때 해당값에 의존해선 안된다.
    이를 수정하기 위해 객체보다는 함수를 인자로 사용하는 다른 형태의 setState()를 사용
    이전 state와 업데이트 적용된 시점의 props 를 인자로 받아 사용한다.

    // Correct
     this.setState((state, props) => ({
       counter: state.counter + props.increment
     ));
  • state 업데이트는 병합된다.

    setState()를 호출할 때 리액트는 제공한 객체를 현재 state로 병합한다.

     componentDidMount() {
         fetchPosts().then(response => {
           this.setState({
             posts: response.posts
           });
         });
    
         fetchComments().then(response => {
           this.setState({
             comments: response.comments
           });
         });
       }

    병합은 얕게 이루어지기 때문에 this.setState({comments})는 this.state.posts에 영향을 주진 않지만 this.state.comments는 완전히 대체된다.

  • 데이터는 아래로 흐른다.

    • state 캡슐화, 로컬
      특정 컴포넌트가 유상태, 무상태인지 알수없고, 함수나 클래스로 정의되었는지 관심을 가질 필요가 없다.

    • 하향식, 단방향식 데이터 흐름
      모든 스테이트는 항상 특정한 컴포넌트가 소유. 그 state 로부터 파생된 ui 또는 데이터는 오직 트리구조에서 자신의 아래에있는 컴포넌트에만 영향을 미친다.

    • 모든 컴포넌트는 완전히 독립적이다.
      참고 코드

이벤트 처리

  • 리액트에서 이벤트는 캐멀케이스를 사용

  • JSX를 사용하여 문자열이 아닌 함수로 이벤트 핸들러를 전달

  • 리액트에선 false를 반환해도 기본 동작을 방지할 수 없다.

    반드시 preventDefault를 명시적으로 호출해야 한다.

    function ActionLink() {
      function handleClick(e) {
        e.preventDefault();
        console.log('The link was clicked.');
      }
    
      return (
        <a href="#" onClick={handleClick}>
          Click me
        </a>
      );
    }
  • 리액트는 W3C에 따라 합성 이벤트를 정의한다. 브라우저 호환성에 대해 걱정할 필요가 없다.

  • 리스너를 추가하기 위해 addEventListener를 호출할 필요가 없다.

  • 클래스 컴포넌트에서 이벤트 정의는 보통 클래스의 메서드로 만든다.

  • on,off 상태를 토글하는 버튼을 렌더링하는 토글 컴포넌트

    class Toggle extends React.Component {
      constructor(props) {
        super(props);
        this.state = {isToggleOn: true};
    
        // 콜백에서 `this`가 작동하려면 아래와 같이 바인딩 해주어야 합니다.
        this.handleClick = this.handleClick.bind(this);
      }
    
      handleClick() {
        this.setState(state => ({
          isToggleOn: !state.isToggleOn
        }));
      }
    
      render() {
        return (
          <button onClick={this.handleClick}>
            {this.state.isToggleOn ? 'ON' : 'OFF'}
          </button>
        );
      }
    }
    
    ReactDOM.render(
      <Toggle />,
      document.getElementById('root')
    );

제어컴포넌트

HTML에서 <input>, <textarea>, <select>와 같은 폼 엘리먼트는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트한다.

React에 의해 값이 제어되는 입력 폼 엘리먼트를 “제어 컴포넌트 (controlled component)“라고 한다.

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

textarea 태그

class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 'Please write an essay about your favorite DOM element.'
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('An essay was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Essay:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

React에서 <textarea>는 value 어트리뷰트를 대신 사용합니다. 이렇게하면 <textarea>를 사용하는 폼은 한 줄 입력을 사용하는 폼과 비슷하게 작성

select 태그

React에서는 selected 어트리뷰트를 사용하는 대신 최상단 select태그에 value 어트리뷰트를 사용

class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('Your favorite flavor is: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Pick your favorite flavor:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">Grapefruit</option>
            <option value="lime">Lime</option>
            <option value="coconut">Coconut</option>
            <option value="mango">Mango</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}
  • select 태그에 multiple옵션을 허용하면 value어트리뷰트에 배열을 전달할 수 있다.
<select multiple={true} value={['B', 'C']}>

file input 태그

<input type="file" />

  • 값이 읽기 전용이기 때문에 비제어 컴포넌트이다.

다중입력제어

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          Is going:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          Number of guests:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}
  • 주어진 input 태그의 name에 일치하는 state를 업데이트하기 위해 ES6의 computed property name 구문을 사용
this.setState({
  [name]: value
});
  • React 애플리케이션 안에서 변경이 일어나는 데이터에 대해서는 “진리의 원천(source of truth)“을 하나만 두어야 한다.
  • 보통의 경우, state는 렌더링에 그 값을 필요로 하는 컴포넌트에 먼저 추가된다. 그러고 나서 다른 컴포넌트도 역시 그 값이 필요하게 되면 그 값을 그들의 가장 가까운 공통 조상으로 끌어올리면 된다.
  • state를 끌어올리는 작업은 양방향 바인딩 접근 방식보다 더 많은 “보일러 플레이트” 코드를 유발하지만, 버그를 찾고 격리하기 더 쉽게 만든다는 장점이 있다.

합성 , 상속

컴포넌트에서 다른 컴포넌트를 담기

function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}
    </div>
  );
}
  • 어떤 자식 엘리먼트가 들어올지 예상할수 없는 경우 범용적 박스 역할을 하는 Sidebar 혹은 Dialog와 같은 컴포넌트 사용을 많이 한다.이런 경우 children prop 을 사용하여 자식 엘리먼트에 출력을 그대로 전달
function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        Welcome
      </h1>
      <p className="Dialog-message">
        Thank you for visiting our spacecraft!
      </p>
    </FancyBorder>
  );
}
  • 리액트 엘리먼트들은 단지 객체이기 때문에 prop으로 전달할 할 수 있다.

상속

  • UI가 아닌 기능을 여러 컴포넌트에서 재사용하기를 원한다면, 별도의 JavaScript 모듈로 분리하는 것이 좋다.

  • 컴포넌트에서 해당 함수, 객체, 클래스 등을 import 하여 사용할 수 있다.

생명주기 Life Cycle

  • state and Lifecycle

    • state는 props와 유사하지만 비공개이며 컴포넌트에 의해 완전히 제어된다.
  • 클래스 컴포넌트로의 변경

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
  • 클래스에 로컬 State 추가하기

  • 생명주기

    • 컴포넌트가 돔에 처음 렌더링 될 때 (마운팅)
    • 생성된 돔이 삭제될 때 (언마운트)
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

	componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

	tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
  • 생명주기 메서드

    • componentDidMount
    • componentWillUnmount
  • this.props가 React에 의해 스스로 설정되고 this.state가 특수한 의미가 있지만, 타이머 ID와 같이 데이터 흐름 안에 포함되지 않는 어떤 항목을 보관할 필요가 있다면 자유롭게 클래스에 수동으로 부가적인 필드를 추가해도 된다. (this.timerID)

profile
기본에 충실하고 싶습니다. #Front-end-developer

0개의 댓글