State and Lifecycle

y0ung·2020년 12월 12일
0

React

목록 보기
5/13
post-thumbnail

React컴포넌트 안의 state와 생명주기

다음 코드는 렌더링된 출력값을 변경하기위해 ReactDOM.render()을 호출한것이다.

function tick() {
  const element = (
    <h1>Hello,world</h1>
    <h2>It is {new Date().toLocaleTimeString()}.</h2>
  );
  ReactDOM.render(
    element,
    document.getElementByid('root')
  );
}

setInterval(tick, 1000);

코드의 캡슐화

아래의 예제는 컴포넌트를 재사용하고, 캡슐화 하는 코드이다. 이 컴포넌트는 스스로 타이머를 설정하고, 매초 업데이트 한다.

function Clock(){
  return (
    <div>
      <h1>Hello,world</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  )
}

function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);

함수에서 클래스로 변환하기

위의 Clock() 과 같은 함수 컴포넌트를 다섯단계의 클래스로 변환할수 있다.

1. `React.Component`를 확장하는 동일한 이름의 ES6 `class` 생성
2. `render()` 메서드 추가
3. 함수의 내용을 `render()` 메서드 안으로 옮긴다.
4. `render()` 내용 안에있는 `props`를 **`this.props`** 로 변경한다.
5. 남아 있는 빈 함수 선언을 삭제한다.
class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

render 메서드는 업데이트가 발생할 때마다 호출되지만, 같은 DOM노드로 을 렌더링 하는 경우 Clock 클래스의 단일 인스턴트만 사용된다. 이것은 로컬 state와 생명주기 메서드와 같은 부가적인 기능을 사용할 수 있게 해준다.

클래스에서 로컬 State추가하기

class Clock extends React.Component {
  // 2. this.state를 지정하는 class constructor를 추가한다.
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
  
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        // 1.this.props.data => this.state.data로 변경한다.
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

props를 기본 constructor에 전달하는 방법

constructor(props) {
  super(props);
  this.state = { date: new Date() }
}

클래스 컴포넌트는 항상 props를 기본으로 constructor를 호출해야 한다.

최종적인 결과는 다음과 같다

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

생명주기 메서드를 클래스에 추가하기

많은 컴포넌트가 있는 애플리케이션에서 컴포넌트가 삭제될 때 해당 컴포넌트가 사용중이던 리소스를 확보하는 것은 중요하다!!

Clock이 처음 DOM에 렌더링 될 때마다 타이머를 설정하려고 하는데 이것을 React에서는 "마운팅" 이라고 한다. 또한 Clock에 의해 생성된 DOM이 삭제될 때마다 타이머를 해제 하려고 한다.

컴포넌트 클래스에서 특별한 메서드를 선언하여 컴포넌트가 마운트되거나 언마운트 될 때 일부 코드를 작동할 수 있다.

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() 메서드는 컴포넌트 출력물이 DOM에 렌더링 된 후에 실행됨.

  • tick()메소드는 컴포넌트 로컬 state를 업데이트 하기 위해 this.setState()를 사용한다.

setState()
state값을 변경해준후 업데이트 해준다.

State를 올바르게 사용하기

직접 State를 수정하지 말아라!

// 렌더링하지  않는다.
this.state.comment = 'Hello';

setState()를 사용하자!!

// 렌더링 한다.
this.setState({comment: 'Hello'})

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

비동기적일수 있는 state의 업데이트

다음 예제에서 this.props와 this.state가 비동기적으로 업데이트 될수 있기 때문에 다음 state를 계산할 때 해당 값에 의존하면 안된다

//Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
})

보다더 좋은 방법은 객체보다는 함수를 인자로 사용하는 다른 형태의 setState()를 사용한다. 그 함수는 이전 state를 첫번째 인자로 받아 들일 것이고, 업데이트가 적용된 시점의 props를 두번째 인자로 받아들일것이다.

this.setState((state,props) => ({
  counter: state.counter + props.increment
}))

state 업데이트는 병합된다.

setState()를 호출할 때 현재 state로 병합한다.

예를 들어, state는 다양한 독립적인 변수를 포함할 수 있다.

constructor(props) {
  super(props);
  this.state = {
    posts: [],
    comments: []
  };
}

별도의 setState() 호출로 이러한 변수를 독립적으로 업데이트 할 수 있다.

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

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

데이터는 아래로 흐른다.

보모 컴포넌트나 자식 컴포넌트 모두 특정 컴포넌트가 유/무 상태인지 알수 없고, 함수나 클래스로 정의 되었는지에 대해서 관심을 가질 필요가 있다.

이러한 이슈로 state는 로컬 또는 캡슐화라고 불리운다. state가 소유하고 설정한 컴포넌트 이외에는 어떠한 컴포넌트에도 접근할 수 없다.

컴포넌트는 자신의 state를 자식 컴포넌트에 props로 전달 할수 있다.

<FormattedDate date={this.state.date} />

FormattedDate 컴포넌트는 date를 자신의 props로 받을 것이고 이것이 Clock의 state로부터 왔는지, Clock의 props에서 왔는지, 수동으로 입력한 것인지 알지 못한다.

function FormattedDate(props) {
  return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}

일반적으로 이를 하향식 또는 단방향식 데이터 흐름이라고 한다. 모든 state는 항상 특정한 컴포넌트가 소유하고 있으며 그 state로 부터 파생된 UI또는 데이터는 오직 트리구조에서 자신의 아래에 있는 컴포넌트에만 영향을 미친다.


참고

해당 포스팅은 실전 리엑트 공식 홈페이지 https://ko.reactjs.org/
리엑트 자습서 https://ko.reactjs.org/docs/state-and-lifecycle.html 에서 참고하여 작성했습니다.

profile
어제보다는 오늘 더 나은

0개의 댓글