강백호같은개발자

React State와 생명주기 본문

- 배움은 개발을 거들뿐(TIL)

React State와 생명주기

쨜리 2020. 7. 9. 11:27

React에서 State란 컴포넌트가 갖는 상태를 말합니다.

컴포넌트의 상태를 관리할 수 있습니다. 

객체의 형태로 컴포넌트 내에서 보관하고 관리합니다.

State가 작성되기 위해서는 class 컴포넌트로 작성되어야 합니다.

그리고 State 값을 변경할 땐 반드시 setState 메소드를 사용해야합니다.

State가 변경되면 랜더 함수가 다시 실행됩니다.

 

하나씩 살펴봅시다.

 

째깍 시계 코드를 살펴봅시다. 

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

tick 함수안에서 element를 선언하고 있는데,

시간을 구현하는 부분은 또 내부에 있기 때문에 재사용성이 떨어진다고 볼 수 있습니다.

이제 Click이라는 컴포넌트로 나눠서 구현해 봅시다.

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

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

Click컴포넌트가 엘리먼트를 반환하고 있습니다. 

그리고 tick 함수에서는 사용자정의 컴포넌트를 사용하면서 속성으로

{date: new Date()} 를 props 객체로 넘겨주고 있네요.

 

그렇지만, Clock이 타이머를 설정하고 매초 UI를 업데이트 하는 것은

setInterval에서 구현되고 있기 때문에 이를

Clock 컴포넌트의 구현 세부사항으로 옮겨보겠습니다. 

이상적으로 바라는 모습은 setInterval을 이용하지 않기 때문에 

함수 tick도 사용하지 않는 형태가 되겠죠?

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

이렇게 구현하기 위해서는 Clock 컴포넌트에 'state'를 추가해야 합니다.

'state'는 props와 유사하지만 비공개이고, 컴포넌트에 의해 완전히 제어됩니다.

 

단, state는 Class 컴포넌트에서만 사용되기 때문에, 

함수 컴포넌트를 Class로 변경해서 사용해야합니다.

 

 

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

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

1. React.Component 를 확장(상속extends)하는 동일한 이름의 ES6 class를 생성합니다.

2. render()라고 불리는 빈 메서드를 추가합니다.

3. 함수의 내용을 render() 메서드 안으로 옮깁니다.

4. render() 내용 안에 있는 props를 this.props로 변경합니다. 

5. 남아있는 빈 함수 선언을 삭제합니다.

class Clock extends React.Component {
  render(){
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
    </div>
  }
}

 

클래스에 로컬 State 추가하기

세단계에 걸쳐서 State를 추가해보겠습니다.

 

1. render() 메서드 안에 있는 this.props.date를 this.state.date로 변경합니다.

2. 초기 this.state를 지정하는 class constructor를 추가합니다.

여기서 어떻게 props를 기본 constructord에 전달하는지 유의해서 봅니다. 

super(props)를 이용해서 부모 클래스의 constructor를 가져오고 있네요.

클래스 컴포넌트에서는 항상 props로 기본 constructor를 호출해야합니다.

 

3. <Clock /> 요소에서 date prop을 삭제합니다.

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);
  }
  
  render(){
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    )
    
ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);
  }
}

이러한 메서드들은 생명주기 메서드라고 불립니다.

 

componentDidMount() 메서드는 컴포넌트 출력물이 DOM에 렌더링 된 후에 실행됩니다.

이 장소가 타이머를 설정하기에 좋은 장소입니다. 

 

this(this.timerID)에서 어떻게 타이머 ID를 제대로 저장하는지 주의하세요.

this.props가 React에 의해 스스로 설정되고 this.state가 특수한 의미가 있지만, 

타이머 ID와 같이 데이터 흐름 안에 포함되지 않는 어떤 항목을 보관할 필요가 있다면 

자유롭게 클래스에 수동으로 부가적인 필드를 추가해도 됩니다.

 

지금 그래서 this.timerID를 수동적으로 추가한 것입니다.

 

그리고 componentWillUnmount() 생명주기 메서드 안에 있는 타이머를 분해합니다. 

 

마지막으로 Clock컴포넌트가 매초 작동하도록 하는 tick() 이라는 메서드를 구현해봅니다.

이것은 컴포넌트 로컬 state를 업데이트하기 위해 this.setState()를 사용합니다.

 

State를 올바르게 사용하기

1. 직접 State를 수정하지 마세요.

대신에 setState()를 사용하세요

this.setState({commnet: 'Hello'});

this.state를 지정할 수 있는 유일한 공간은 바로 constructor 입니다.

 

2. State 업데이트는 비동기적일 수도 있습니다.

 

3. State 업데이트는 병합됩니다. 

 

 

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

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

 

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

 

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

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

FormattedDate 컴포넌트는 date를 자신의 props로 받을 것이고, 이것이 Clock의 state로부터왔는지 

Clock의 props에서왔는지 수동으로 입력한 것인지 알지 못합니다.

Comments