본문 바로가기

JavaScript/React

5. State와 생명주기

State란?

React 컴포넌트에서 다루는 데이터는 Props와 State 두 가지로 나뉜다.

 

Props는 부모 컴포넌트가 자식 컴포넌트에 값을 전달할 때 사용하는 것으로 "읽기 전용"이다. 자식 컴포넌트는 값을 받기만 하고 직접 수정할 수 없다.

이 점을 이용해서 변경되지 말아야 할 데이터를 효율적으로 관리할 수 있다.

 

State는 컴포넌트 자기 자신이 갖고 있는 값이다. 내부에서 선언하고 내부에서 변경할 수 있다. props와 비교해서 "쓰기 전용"이라고 볼 수 있다.

 


React doc 예시

React 공홈에 나와있는 문서 내용을 따라가보자!

 

3. 엘리먼트 렌더링에서 만든 시계 예제를 살펴보자.

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

setInterval 함수를 이용해서 1000m/s초마다 tick 함수를 실행시키고, tick함수는 ReactDOM.render를 실행시켜 화면을 계속 바꿔준다.

 

React 앱은 ReactDOM.render()를 한번만 호출하는데 보이는 코드는 setInterval을 통해 1000m/s마다 ReactDOM.render()를 호출한다.

 

굳이 setInterval을 써? 그럼 변화 있을 때 마다 setInterval설정해줄래? 우리는 컴포넌트 스스로 타이머를 설정하고, 매 초 스스로 업데이트 하도록 만들어줄 거다.

이점은? 1. 컴포넌트 내부에서 완전한 제어가 가능하다 → 코드가 깔끔해짐 2. 캡슐화(숨김)가 가능하다. 3. 사용하는 쪽과 구현하는 쪽을 철저하게 분리시켜 양쪽의 편의성을 높인다. → 기능 분리, 코드 깔끔

 

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

구현하기 전에 코드를 좀 더 깔끔하게 만들었다. 시계 기능을 하는 함수 Clock과 렌더링해주는 tick함수로 나눴다.

 

함수에서 클래스 변환하기

State는 class 컴포넌트만 가질 수 있다. 그래서 우리는 해당 코드를 함수에서 클래스로 바꿔준다. (그래서 함수형 컴포넌트를 stateless component라고 부르기도 한다)

 

과정은 다음과 같다.

  1. React.Component를 extends하는 class 생성
  1. render() 메서드를 추가
  1. clock() 내용을 render() 메서드에 옮기고
  1. props를 this.props로 바꾼다. ( 해당 class 내의 props임을 밝혀줘야함)
  1. clock() 필요없는 거 정리

 

class Clock extends React.Component{ render(){ <div> <h1> Hello, World! </h1> <h2> It is {this.props.date.toLocaleTimeString().</h2> </div> } } function tick(){ ReactDOM.render( <Clock date={new Date()} />, document.getElementById('root') ); } setInterval(tick, 1000);

Clock은 이제 클래스입니다. 로컬 state와 생명 주기 메서드와 같은 부가적인 기능을 사용할 수 있다.

클래스에 로컬 state 추가하기

 

로컬state 쓰려면 추가해야겠지? props빼고 state로 바꿔줘야겠지?

 

  1. render() 메서드 안에 있는 this.props.date를 this.state.date 변경해준다.
  1. 초기 값을 지정하는 class constructor 추가한다. 현재 값도 초기화 this.state = ;
  1. <Clock /> 요소에서 date props를 삭제한다. 제대로 분리해주자.

 

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.toLocalTimeString()}.</h2> </div> ) }; } ReactDOM.render( <Clock />, document.getElementById('root') );

 


생명주기 메서드 추가

애플리케이션에서 컴포넌트가 많으면 많을수록 삭제 된 컴포넌트가 쓰던 리소스를 확보하는 게 중요하다.

 

우리는 지금 <Clock/>이 DOM에 렌더링 될 때 마다 타이머를 설정하려고 한다. 이를 "마운팅"이라고 부른다. DOM이 삭제 될 때 타이머를 해제하려고 하는데 이를 "언마운팅"이라 부른다.

 

컴포넌트가 마운트되거나 언마운트 될 때 특정 코드를 작동시킬 수 있다.

 

React Lifecycle Methods diagram
Fully interactive and accessible React Lifecycle Methods diagram.
https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

컴포너트 출력물이 DOM에 Render될 때 componentDidMount() 사용 → 타이머 설정하는 데 사용하면 적절할 듯 하다.

 

컴포넌트가 제거되기 직전에 무언가 실행하고자 한다면? componentWillUnmount() 사용

→ 타이머 해제하는데 적절할 듯 하다.

 

이런 메서드를 "생명주기 메서드"라고 부른다. 주기... 시작과 끝이 있는....

 

 

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

 

 

마지막으로 Clock 컴포넌트가 매초 작동하도록 tick()이란 메서드를 구현했다. ★state 업데이트는 반드시 this.setState()를 사용한다.

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

 

 

최종 코드

 

class Clock extends React.Componet{ 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.toLocalTimeString()}.</h2> </div> ); } } ReacteDOM.render( <Clock />, document.getElementById('root') );

 

요약:

  1. <Clock />가 ReactDOM.render()로 전달되었을 때 React는 Clock 컴포넌트의 constructor를 호출한다. Clock이 현재 시각을 표시해야 하기 때문에 현재 시각이 포함된 객체로 this.state를 초기화 한다. (나중에 이 state가 업데이트 됨)
  1. React는 Clock 컴포넌트의 render() 메서드를 호출한다. 이를 통해 React는 화면에 표시할 내용을 알게 된다. 어? 출력값이 달라졌네? React는 Clock의 렌더링 출력값을 일치시키기 위해 DOM을 업데이트 한다.
  1. Clock 출력값이 DOM에 삽입되면 React는 componetDidMount() 생명주기 메서드를 호출한다. (DOM 생성 될 때니까) 그럼 내부에 tick() 메서드를 호출하기 위한 타이머를 설정하라고 요구한다.
  1. 브라우저는 1000m/s마다 tick() 메서드를 호출한다 호출 → UI 업데이트 → 엇? <Clock/>변경됐네? → 2에서 4까지 반복하며 업데이트
  • 만약 Clock 컴포넌트가 DOM으로부터 한번이라도 삭제되면, React는 componentWillUnmount() 생명주기를 호출해서 타이머를 멈춘다.

 

 


데이터는 아래로

 

 

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

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

FormattedDate 컴포넌트는 date를 props로 받게 됩니다.그리고 이게 어디서, 어떻게 왔는지 알 수 없다.

이 때문에 state의 특징으로 캡슐화를 이야기할 수 있다.

또한 부모에서 자식으로 데이터가 흐르기 때문에이를 "하향식(top-down)" 또는 "단방향식" 데이터 흐름이라고 한다.

그리고 이렇게 아래로 흐른 컴포넌트는 부모가 같다해도 서로는 완전히 독립적이다.

function App() { return ( <div> <Clock /> <Clock /> <Clock /> </div> ); } ReactDOM.render( <App />, document.getElementById('root') );

각 Clock은 자신만의 타이머를 설정하고 해지하게 됩니다.

 

 

 

 

출처

State and Lifecycle - React
이 페이지는 React 컴포넌트 안의 state와 생명주기에 대한 개념을 소개해 줍니다. 자세한 컴포넌트 API 레퍼런스는 여기 에서 찾을 수 있습니다. 이전 섹션에서 다뤄본 째깍거리는 시계 예시를 다시 살펴보겠습니다. 엘리먼트 렌더링에서는 UI를 업데이트하는 한 가지 방법만 배웠으며, 렌더링 된 출력값을 변경하기 위해 ReactDOM.render() 를 호출했습니다. 이 섹션에서는 Clock 컴포넌트를 완전히 재사용하고 캡슐화하는 방법을 배울 것입니다.
https://ko.reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

 

 

반응형

'JavaScript > React' 카테고리의 다른 글

create-react-app  (0) 2021.03.08
리액트 시작하기-기본적으로 필요한 툴  (0) 2021.03.08
11. 합성 vs 상속  (0) 2021.02.05
6. 이벤트 처리  (0) 2021.02.02
5.1 State 올바르게 사용하기  (0) 2021.02.01