[React Docs 번역] State and Lifecycle

김승엽
Berkbach
15 min readApr 15, 2020

--

Photo by Fiona Smallwood on Unsplash

State and Lifecycle

이 페이지에서는 React 컴포넌트의 State와 Lifecycle 의 개념을 소개합니다. 이전 글에서 작성했던 시계 예제를 봅시다. ReactDOM.render() 를 호출해서 UI를 업데이트하는 방법을 배웠습니다.

이번 시간에는 재사용가능하고 캡슐화된 Clock 컴포넌트를 만들어보겠습니다. Clock 컴포넌트는 타이머를 설정하고, 매 초 스스로 업데이트 할 것입니다. 다음 처럼 캡슐화를 진행할 수 있습니다.

하지만 타이머를 설정하고 매 초 UI를 업데이트 하는 것이 Clock 의 구현 세부사항이 되어야 한다는 사실을 놓치고 있습니다. 이상적으로 우리는 코드를 한번만 작성하고 Clock 이 스스로 업데이트하길 원합니다:

이것을 실행 하기 위해서 Clock 컴포넌트에 “state” 를 추가 해야 합니다.
State는 Props와 비슷하지만 private 하고 컴포넌트에 의해 조작됩니다.

Converting a Function to a Class

5 단계로 Clock 과 같은 함수형 컴포넌트를 클래스형 컴포넌트로 바꿀 수 있습니다.

  1. 같은 이름으로 된 React.Component 를 확장하는 ES6 class를 작성합니다.
  2. render() 메소드 하나를 추가합니다.
  3. 함수 의 내용을 render() 메소드 안으로 옮깁니다.
  4. render() 안의 propsthis.props 로 바꿉니다.
  5. 남아 있는 함수 선언문을 지워줍니다.

Clock 이 함수형에서 클래스형으로 바뀌었습니다.

render() 메소드는 업데이트가 될 때 마다 호출 될 것 입니다. 하지만 Clock 이 같은 DOM 노드에서 렌더링 되기 때문에 하나의 Clock 인스턴스만 사용됩니다. 이것은 우리에게 state, lifecycle 같은 추가 기능을 사용하게 해줍니다.

Adding Local State to a Class

3 단계를 거쳐서 데이터를 props에서 state로 바꾸겠습니다.

  1. render() 메소드 안의 this.props.datethis.state.date 로 변경합니다.

2. 초기 this.state를 지정하는 class constructor (생성자) 를 추가합니다.

여기서 어떻게 props를 기본 생성자에 전달하는지 확인하세요.

클래스형 컴포넌트의 props는 항상 기본 생성자에 의해 호출됩니다.

3. Clock element의 date prop을 지워줍니다.

타이머는 나중에 추가하도록 하겠습니다. 결과는 이렇습니다.

이어서 Clock 의 타이머를 설정하고 매 초 업데이트 하도록 하겠습니다.

Adding Lifecycle Methods to a Class

많은 컴포넌트가 있는 애플리케이션에서 컴포넌트가 제거될 때 컴포넌트의 리소스들을 확보하는 것은 매우 중요합니다.

우리는 DOM에 Clock 이 처음 렌더링 될 때 타이머가 설정 되기를 원합니다. 이것을 React에서 mounting 이라고 부릅니다.

또한 Clock 에 의해 생성된 DOM 이 지워 질 때 마다 타이머가 해제 되기를 원합니다. 이것을 React에서 unmounting 이라고 부릅니다.

컴포넌트가 mount , unmount 될 때 마다 실행되는 특별한 코드를 컴포넌트 클래스에서 선언할 수 있습니다.

이것을 “LifeCycle 메소드” 라고 부릅니다.

componentDidMount() 메소드는 컴포넌트의 출력값이 DOM에 렌더링이 된 후에 실행합니다. 이 부분이 타이머를 설정하기 좋은 곳입니다.

this (this.timerID)에서 timer ID를 저장하는 방법을 주의하세요.

this.props 는 React에 의해 스스로 설정하고 this.state 가 특별한 의미를 갖지만, 타이머 ID와 같이 데이터 흐름 안에 포함되지 않는 어떤 것을 보관할 필요가 있다면 자유롭게 클래스에 수동으로 부가적인 필드를 추가해도 됩니다.

componentWillUnmount 메소드에서 타이머를 지워보겠습니다.

마지막으로 Clock 컴포넌트의 tick() 을 매 초 마다 호출하는 메소드를 추가하겠습니다.

컴포넌트의 로컬 state를 업데이트하기 위해서 this.setState() 를 사용하겠습니다.

이제 타이머가 매 초 업데이트 됩니다. 메소드들이 호출 되는 순서를 빠르게 확인하겠습니다.

  1. <Clock />ReactDOM.render() 로 전달됐을 때, React가 Clock 컴포넌트의 생성자를 호출합니다. Clock 이 현재 시간을 보여줘야하기 때문에 현재 시간을 포함한 객체를 초기화합니다. 우리는 이 state를 나중에 업데이트 합니다.
  2. 다음 React가 Clock 컴포넌트의 render() 메소드를 호출합니다. 이것을 통해 React가 무엇을 화면에 띄어야 할지 알게 됩니다. 다음 React가 Clock 의 렌더링 출력물에 맞춰서 DOM을 업데이트합니다.
  3. Clock 의 출력물이 DOM에 추가될 때, React는 componentDidMount() 메소드를 호출합니다. 그 안에서는, Clock 컴포넌트는 1초의 한번씩 tick() 메소드를 호출하기 위한 타이머를 설정하도록 브라우저에게 요청합니다.
  4. 매 초마다 브라우저가 tick() 메소드를 호출합니다. 그 안에서는 Clock 컴포넌트는 setState() 에 현재 시간을 포함하는 객체를 호출해서 UI 업데이트를 진행합니다. setState() 호출 덕분에 React는 state의 변화를 알게 됩니다. 그리고 화면에 표시할 내용을 알기 위해서 render() 메소드를 다시 호출합니다. 이번에는 render() 메소드의 this.state.date 는 다르기 때문에 렌더링의 출력물은 업데이트된 시간을 포함할 것입니다. React는 그에 맞춰 DOM을 업데이트 합니다.
  5. 만약 Clock 컴포넌트가 DOM에서 한번이라도 삭제된다면, React는 타이머를 멈추기 위해서 componentWillUnmount() 를 호출합니다.

Using State Correctly

setState() 에 대해 알아야할 3가지가 있습니다.

Do Not Modify State Directly

이것은 re-rendering이 일어나지 않습니다.

대신, setState() 를 사용해야 합니다.

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

State Updates May Be Asynchronous

React는 성능을 위해 여러 setState() 호출을 단일 업데이트로 한꺼번에 처리할 수 있습니다.

this.propsthis.state가 비동기적으로 업데이트될 수 있기 때문에 다음 state를 계산할 때 해당 값에 의존해서는 안 됩니다.

이 코드는 counter 업데이트에 실패할 수 있습니다.

고치기 위해서 객체대신 함수형으로된 setState() 의 두번째 양식을 사용합니다. 이 함수는 첫번째 인자로 이전의 state를 받습니다. 두번째 인자로 업데이트된 props를 받습니다.

위에서는 arrow function을 사용했지만, 정규 함수도 작동합니다.

State Updates are Merged

setState() 가 호출되면 React는 당신이 객체를 현재 state에 병합합니다.

state가 몇 개의 독립적인 변수들을 포함할 수 있습니다.

분리된 setState() 호출로 독립적으로 업데이트를 할 수 있습니다:

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

The Data Flows Down

부모 컴포넌트나 자식 컴포넌트 모두 특정 컴포넌트의 state유무를 알 수 없고, 그들이 함수나 클래스로 정의 되었는지에 대해서 관심을 가질 필요가 없습니다. 이것이 state가 대게 로컬 또는 캡슐화로 불리는 이유입니다. state를 보유하거나 설정하지 않은 다른 컴포넌트가 접근할 수 없습니다. 컴포넌트가 본인의 state를 자식 컴포넌트에게 props 로 전달 할 수도 있습니다.

이것은 사용자 정의 컴포넌트에서도 사용할 수 있습니다.

FormattedDate 컴포넌트는 date를 props로 받을 것이고 이것이 Clock의 state에서왔는지, Clock의 props에서 왔는지, 하드코딩 됐는지 모릅니다.

이것은 일반적으로 하향식 ( “top-down” ) 또는 단방향 ( “unidirectional” ) 데이터 흐름으로 불립니다. 모든 state는 항상 어떤 특정 컴포넌트가 갖습니다. 그리고 그 state에서 파생된 모든 데이터 또는 UI는 트리 구조에서 “아래” 컴포넌트에게만 영향을 미칠 수 있습니다.

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

독립된 모든 컴포넌트를 보기 위해선, 우리는 3개의 Clock을 렌더링 하는 App 컴포넌트를 만들 수 있습니다.

Clock 컴포넌트는 스스로 타이머를 설정하고 독립적으로 업데이트합니다.

React 앱에서 컴포넌트가 상태가 있는지 없는지는 시간에 따른 컴포넌트의 실행 세부 사항 결정됩니다. 무상태 컴포넌트 안에서 유상태 컴포넌트를 사용할 수 있고, 그 반대의 경우도 가능합니다.

📚 정리 및 참고 자료

  1. Lifecycle
  2. State와 Props는 비동기적으로 작동합니다.
  3. State를 변경할 때는 setState() 를 사용해야 합니다.

--

--