리액트 State and Lifecycle : 상태와 생명주기 : react (미완)

horiz.d·2021년 12월 19일
0

리액트 꿀단지

목록 보기
9/41

State 요약 참조 :

https://velog.io/@hidaehyunlee/React-State-%EB%9E%80


State and Lifecycle

앞서, 리액트 렌더링 요소 : React Elements & React DOM 에서 UI를 업데이트 하는 한가지 방식을 배워보았고, 렌더링 된 출력값을 변경하기 위해 ReactDOM.render()을 호출했다.

다만 컴포넌트의 pure한 특성으로 내부에서 props를 변경하지 못하여 UI의 동적 변경에서 한계점을 맞이했다.

아래엔 기존의 렌더링 방식을 사용한 예제와 코드를 명시해놓았고, state의 핵심 특징과 state를 활용한 개선 목표 또한 명시하였다.


우리는 아래의 코드로 만들어진 상기 페이지의 Ticking Clock의 Rendered output을 바꾸기 위한 방법으로,
.
즉 UI를 업데이트 하기 위한 방법으로, 시간이 흐르는 단위인 매 초마다 ReactDOM.render() 을 사용하여 Elements를 렌더링 해 주었다.
.
하지만 이러한 비효율적인 방식을 개선하고자 저번의 ticking 예제를 변형하며,
State 를 사용하여 UI를 업데이트 하는 방법을 배우고자 한다.



기존 Ticking 코드 : ReactDOM.render()을 매 초 호출

function tick() {
  const element = (
  <div>
      <h1>Hello, horiz.d</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
  </div>
  );
  ReactDOM.render(
  element,
  document.getElementById('root'));
}
.
setInterval(tick, 1000);

직전의 예제는 ReactDOM.render()를 매 초 호출하며 변화사항을 DOM이 매 초 새로 렌더링 하여 UI를 업데이트 했다.

일단 하나의 함수 tick()을 아래와 같이 Clock컴포넌트tick()함수로 분할 해준다.

function Clock(props) { // 컴포넌트
  return (
  <div>
      <h1>Hello, horiz.d</h1>
      <h2>It is {props.date.toLocaleTimeString()}.</h2> 
  </div>
  );
}
.
function tick() { //함수
  ReactDOM.render(
  <Clock date={new Date()} />, // Clock 컴포넌트 사용
    document.getElementById('root')
  );
}
// setInterval이 callback으로 tick함수를 사용했고
//tick내에 삽입된 <Clock /> 컴포넌트의 렌더링이 수행됨
setInterval(tick, 1000); 



State를 사용한 개선 목표

위의 코드는 여전히 가장 중요한 요구사항이 결여돼있다.
우리는 컴포넌트 Clock이 타이머를 설정하여 매 초 UI를 업데이트하도록 만들고자 한다.
.
다시 말해, 가장 이상적으로 한 번만 코드를 작성하고 Clock이 스스로 업데이트 하도록 만들 것 이다.

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

이를 위해, Clock 컴포넌트에 state를 추가 할 것이다.
.


State는 Props와 유사하지만, 비공개이며 컴포넌트에 의해 제어가 가능하다.

직전의 Component & Props 에서 알아봤 듯, Component는 pure하므로 전달받은 props를 존중하여 props의 값을 자신의 내부에서 수정하지 않는다.

이러한 특질로 인해 컴포넌트의 단독 사용은 리액트의 Dyanamic한 앱UI 설계에 제약을 줄 수 있는데,

위에서 언급한 컴포넌트에 의한 제어 가능한 state의 특징을 사용하여 우리는 UI의 Dynamic한 수정을 가능하게 만들 수 있다.




P1. 함수형 컴포넌트 를 클래스형 컴포넌트 로 변환

아래의 다섯 스텝을 거쳐 Clock : 함수형 컴포넌트클래스형 컴포넌트로 변환할 수 있다.

5 STEPs

  1. 기존의 함수형 컴포넌트와 동일한 이름의,
    ES6 class 컴포넌트 ( extends React.Component )를 생성한다.

  2. 생성한 클래스형 컴포넌트render()라고 불리는 단일의 빈 method를 추가한다.

  3. 기존의 함수형 컴포넌트의 body를 Class 컴포넌트 내부의 render()의 { } 내에 옮겨준다.

  4. render() {body} 에 있는 propsthis.props로 교체해준다.

  5. 기존의 함수형 컴포넌트 선언을 제거해준다.



변환 완료된 Clock : Class형 컴포넌트

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

이제 Clock컴포넌트는 함수가 아니라 클래스형 컴포넌트로 정의되었고, 변환된 Clock Component 내에 명시된 render() 메소드는 Update가 발생할 때 마다 호출 될 것이다.

하지만 우리가 <Clock /> 컴포넌트를 동일한 DOM node에서 렌더링 하는 한, Clock Class의 단 하나의 instance 만이 사용 될 것이다.


따라서 local state, lifecycle methods와 같은 추가적인 features를 사용하여 이를 보완해야 한다.



P2. Local State를 클래스 컴포넌트에 추가

해당 process에서는 아래의 3STEP을 따라,
dateprops 에서 state 로 옮겨줄 것 이다.


STEPs

  1. 클래스 내 render() 메소드 안에 있는 this.props.propertythis.state.date로 교체

  2. class컴포넌트 내에 초기의 this.state를 할당하는 class constructor를 추가

  3. 기존 ReactDOM.render()내의 렌더링 대상 요소인 <Clock /> element에서 date prop을 제거


STEP1 : props를 state로 교체

class Clock extends React.component {
  render() {
    return (
      <div>
        <h1>Hello, horiz.d!</h1>
        <h2>It is {this.state.date.toLocalTimeString()}.</h2>
      </div>
    );
  }
}

STEP2 : class constructor 추가 ( 초기 this.state를 할당하는 )

class Clock extends React.component {
  constructor(props) {
    super(props);
    this.state = {date. new Date()};
	}
.
  render() {
    return (
      <div>
        <h1>Hello, horiz.d</h1>
        <h2>It is {this.state.date.toLocalTimeString()}.</h2>
      </div>
      );
  }
}

위의 코드에서, 클래스 컴포넌트가 받은 props를 어떻게 base constructor로 전달했는지 주목하자.

constructor(props){
  super(props);
  this.state = {date: new Date()};
}
  • rule : 클래스 컴포넌트는 항상 props를 가지고 base constructor를 호출해야만 한다.

STEP3 : 렌더링 부 <Clock /> element 에서 date 프로퍼티를 제거

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

이제 아래의 Process 3에서 Clock 컴포넌트가 자신의 timer를 설정하고, 매 초마다 자신을 업데이트하도록 만들 것이다.



P3. Lifcecycle Methods를 클래스 컴포넌트에 추가

많은 컴포넌트를 가진 애플리케이션 안에선, 컴포넌트가 파괴될 때 이들이 차지하는 resource 자원을 해제해 주는 것이 매우 중요하다.
이를 아래의 Mount/Unmount 방식으로 진행할 것 이다.

Mounting : Clock이 Element에서 DOM으로 처음 렌더링 될 때, timer를 설정해주고자 한다.
Unmounting : Clock에 의해 만들어진 DOM이 제거될 때는, 그 timer를 지워주고자 한다.


Component가 mount 혹은 unmount될 때 특정 코드를 실행되게 만들어주기 위해, Component classspecial methods : componentDidMount() & componentWillUnmount()를 선언해 줄 수 있다.

이들을 이르러 Lifecylce methods라고 한다.

Lifecycle Methods 추가

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
 . 
  componentDidMount() {
    해당 부분에 Mount시의 실행 코드를 입력 가능
  }
  componentWillUnmount() {
    해당 부분에 Unmount시의 실행 코드를 입력 가능
  }
 .
 render() {
   <div>
   	<h1>Hello, horiz.d!<h1>
   	<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
   </div>
 }   
  • 위에서 선언된 componentDidMount & componentWillUnmount Lifecycle Method라고 한다.
    .
  • 위의 생명주기 메소드의 내부에 Mount & Unmount 시 실행시킬 코드를 작성해줄 수 있다.

[1] componentDidMount( )

componentDidMount() 메소드는 소속 Component의 산출물이 DOM에 렌더링 된 직후에 실행된다.
그리고 이것은 우리가 계획한 timer를 넣어주기 적절한 곳이다.

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

timer ID를 어떻게 this에 저장했는지 주목하자( this.timerID )

this.props가 React에 의해 설정되고 this.state가 특별한 의미를 가지는데 반해,

componentDidMount() 내에는 timerID처럼, 필요에 따라 직접 추가적인 fields를 만들어 Data Flow에 속하지 않는 무언가를 보관할 수 있다.


그리고 resource 최적화를 위해 아래의 componentWillUnmount( )에서 이들을 제거 해준다.

[2] componentWillUnmount( )

 componentWillUnmount() {
    clearInterval(this.timerID);
  } // this.timerID를 제거해준다.



마침내, Clock 컴포넌트가 매 초마다 동작시킬 tick()이라고 불리는 메소드를 클래스 내에 암시할 것이다.

최종 코드

class Clock extends React.component{
  constructor(props){
    super(props);
    this.state = {date: new Date()};
  }
  // mount LifeCycle메소드 : render 직후 실행됨
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),	// 해당 컴포넌트 내의 tick() 메소드를 실행한다.
      1000
      );
  }
  // unmount LifeCycle메소드 
  componentWillUnmount(){
    clearInterval(this.timerID);
  }
 // 컴포넌트가 매 초 실행하게 되는 tick()메소드
 tick() {
   this.setState({
     date: new Date()
   });
 }
.
render() {
  return (
    <div>
      <h1>Hello, horiz.d!</h1>
      <h2>It is {this.state.date.toLocalTimeString()}.</h2>
    </div>
   );
  }
}
.
ReactDOM.render(
	<Clock />,
	document.getElementById('root')
);

Recap

  1. <Clock />ReactDOM.render()로 전달되었을 때 React는 Clock 컴포넌트의 constructor를 호출합니다. Clock이 현재 시각을 표시해야 하기 때문에 현재 시각이 포함된 객체this.state를 초기화합니다. 나중에 이 state를 업데이트할 것입니다.
    .
  2. React는 Clock 컴포넌트의 render() 메서드를 호출합니다. 이를 통해 React는 화면에 표시되어야 할 내용을 알게 됩니다. 그 다음 React는 Clock의 렌더링 출력값을 일치시키기 위해 DOM을 업데이트합니다.
    .
  3. Clock 출력값이 DOM에 삽입되면, React는 componentDidMount() 생명주기 메서드를 호출합니다. 그 안에서 Clock 컴포넌트는 매초 컴포넌트의 tick() 메서드를 호출하기 위한 타이머를 설정하도록 브라우저에 요청합니다.
    .
  • 매초 브라우저가 tick() 메서드를 호출합니다. 그 안에서 Clock 컴포넌트는 setState()에 현재 시각을 포함하는 객체를 호출하면서 UI 업데이트를 진행합니다.
    .
  • setState() 호출 덕분에 React는 state가 변경된 것을 인지하고 화면에 표시될 내용을 알아내기 위해 render() 메서드를 다시 호출합니다.
    .
  • 이 때 render() 메서드 안의 this.state.date가 달라지고 렌더링 출력값은 업데이트된 시각을 포함합니다. React는 이에 따라 DOM을 업데이트합니다.
    .
  1. Clock 컴포넌트가 DOM으로부터 한 번이라도 삭제된 적이 있다면 React는 타이머를 멈추기 위해 componentWillUnmount() 생명주기 메서드를 호출합니다.



State의 올바른 사용법

State의 직접 수정은 금물

state의 값을 직접 수정해선 안된다, 이경우 리액트는 수정된 state에 따라 다시 렌더링하지 않는다. 대신 setState()를 사용하여 리액트가 이를 인지하고 수정에 따라 새로 렌더링할 수 있도록 해주자.
.
this.state.comment = 'Hello';

// Correct
this.setState({comment: 'Hello'});

State Update 들은 Asynchronous(비동기)일 수 있다.

React는 성능을 위해 여러 setState() 호출을 단일 업데이트로 한번에 처리할 수 있다.
.
this.propsthis.state는 비동기적(Asynchronous)으로 업데이트 될 수 있기 때문에, 다음 state의 계산이 이들에게 의존적이면 안된다.

Wrong Case - Will be fail

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

Correct Case1

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

Correct Case2

this.setState(function(state, props) {
  return {
    counter: state.counter + props.increment
  };
});

State Update 들은 병합 된다.



The Data Flows Down

데이터는 아래로 흐른다

profile
가용한 시간은 한정적이고, 배울건 넘쳐난다.

0개의 댓글