React 공식문서 뜯어보기-State and LifeCycle

8809 단어 ReactReact

엘리먼트 렌더링에서는 UI를 업데이트하는 한가지 방법만 배웠으며, 렌더링 된 출력값을 변경하기 위해서 ReactDOM.render()를 호출했습니다. 하지만 이번에는 컴포넌트를 완전히 재사용하고 캡슐화 하는 방법을 알아보겠습니다. 이 컴포넌트는 스스로 타이머를 설정하고 매초마다 스스로 업데이트 할 수 있도록 만들어 주겠습니다.

먼저 캡슐화를 시작해보겠습니다.

function Clock(props){
  return (
    <>
      <h1>Hello!</h1>
      <h2>It is {props.date.toLocaleTimeString()}.</h2>
    </>
  );
}

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

그러나 여기에는 중요한 요건이 누락되어 있습니다. Clock이 타이머를 설정하고 매초 UI를 업데이트하는 것이 Clock의 구현 세부사항이 되어야합니다. 이것을 구현하기 위해서는 Clock 컴포넌트에 state를 추가해야 합니다.
state는 props랑 유사하지만 state는 컴포넌트 안에서 관리되고 사용할 변수 선언과 비슷하게 사용할 수 있는 객체입니다.


새 로컬 State 추가하기

세 단계에 걸쳐서 date를 props에서 state로 변경하겠습니다.

  1. render() 메서드 안에 있는 this.props.datethis.state.date로 변경합니다.
class Clock extends React.Component {
  render() {
    return (
      <>
        <h1>Hello!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}</h2>
      </>
    );
  }
}
  1. 초기 this.state를 지정하는 class constructor를 추가합니다.
class Clock extends React.Component {
  constructor(props){
    super(props);
    this.state = {date: new Date()};
  }
  
  render() {
    return (
      <>
        <h1>Hello!</h1>
        <h2>It is {this.state.date.toLcaleTimeString()}</h2>
      </>
    );
  }
}

여기서 유의해야 할 것은 클래스 컴포넌트는 항상 props로 기본 constructor를 호출해야 합니다.

지금까지를 종합적으로 보게 된다면 다음과 같은 결과를 볼 수 있습니다.

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

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

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

lifecycle 메서드를 클래스에 추가하기

React Components가 DOM에 마운트되고 해제되는 모든 과정을 React의 lifecycle 이라고 합니다. 컴포넌트 클래스에서 특별한 메서드를 선언해서 컴포넌트가 마운트되거나 언마운트 될 때 일부 코드를 작동할 수 있습니다. 이를 lifecycle method(생명주기 메서드)라고 불립니다.

지금까지의 예시에서 componentDidMount() 메서드는 컴포넌트 출력물이 DOM에 렌더링 된 후에 실행됩니다. 이 메서드 안에 타이머를 설정하기 좋을 것 같습니다.

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

componentWillUnmount() {
  clearInterval(this.timerID)
}

마지막으로 Clock 컴포넌트가 매초 작동하도록 하는 tick()이라는 메서드를 구현하게 된다면 컴포넌트를 완성할 수 있습니다.

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 (
      <>
        <h1>Hello!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}</h2>
      </>
    );
  }
}

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

State를 올바르게 사용하기

setState() 를 사용하면 주의해야할 점이 세가지가 있습니다.

직접 state를 수정해서는 안됩니다.

React는 virtualDOM과 기존의 DOM 모델을 비교해서 바뀐 부분이 있을 때만 필요한 부분만 다시 렌더링을 합니다. 하지만 직접 state를 변경했을 경우 이 부분을 확인 할 수 없기 때문에 state가 변경되어도 컴포넌트를 다시 렌더링 하지 않습니다.

state 업데이트는 비동기적으로 발생합니다.

React는 성능을 위해서 여러개의 setState() 호출을 단일 업데이트로 한꺼번에 처리할 수 있습니다. this.propsthis.state 가 비동기적으로 업데이트 될 수 있기 때문에 다음 state를 계산할 때 해당 값에 의존해서는 안됩니다.

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
      });
    });
  }

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


데이터는 아래로 흐릅니다.

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

이 때문에 state는 종종 로컬 또는 캡슐화라고 불립니다. state가 소유하고 설정한 컴포넌트 이외에는 어떠한 컴포넌트에도 접근할 수 없습니다.

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

트리구조가 props들의 폭포라고 상상하면 각 컴포넌트의 state는 임의의 점에서 만나지만 동시에 아래로 흐르는 부가적인 수원(water source)이라고 할 수 있습니다.

참고자료

React 공식문서

좋은 웹페이지 즐겨찾기