클래스 컴포넌트
16.8미만 작성된 코드는 클래스 컴포넌트가 대다수일 것. 클래스 컴포넌트를 만들려면 클래스를 선언하고 extends로 만들고 싶은 컴포넌트를 extends해야한다.
extends 구문에 넣을 수 있는 클래스는
- React.Component
- React.PureComponent
2가지이다. 이 둘은 클래스 컴포넌트인 shouldComponentUpdate를 다루는 데 차이가 있다.
클래스 컴포넌트 예제
import React from 'react'
interface SampleProps {
required?: boolean
text: string
}
interface SampleState {
count: number
isLimited?: boolean
}
class SampleComponent extends React.Component<SampleProps, SampleState> {
private constructor(props: SampleProps) {
super(props)
this.state = {
count: 0,
isLimited: false,
}
}
private handleClick = () => {
const newValue = this.state.count + 1
this.setState({ count: newValu,e isLimited: newValue >= 10})
}
public render() {
const {
props: { required, text},
state: { count, is Limited },
} = this
return (
<h2>
Sample Component
<div>{required ? '필수' : '필수아님'}</div>
<div>문자 : {text}</div>
<div>count: {count}</div>
<button onClick ={this.handleClick} disabled={isLimited}>
증가
</button>
</h2>
)
}
}
constructor() :
- 컴포넌트 초기화 시점에 호출 state 초기화,
- React.Component의 생성자 함수를 먼저 호출해 필요한 상위 컴포넌트에 접근할 수 있게 도와줌.
- ECMA2022에 추가된 클래스 필드 덕에 별도 초기화(constructor)없이 state 초기화 가능해짐.
props :
- 함수에 인수를 넣는 것과 비슷하게, 컴포넌트에 특정 속성 전달 용도
state :
- 클래스 컴포넌트 내부에서 관리하는 값을 의미
- 항상 객체여야 함.
- 값에 변화가 있을 때마다 리렌더링 발생
메서드 :
- 랜더링 함수 내부에서 사용되는 함수
- DOM에서 발생하는 이벤트와 함께 사용
- 만드는 방법은 크게 3가지
- constructor에서 this 바인딩
일반적인 함수로 메서드를 만들면 this가 undefined가 됨 생성자가 아닌 일반 함수로 호출하면 this에 전역 객체(strict 모드에서는 undefined가 바인딩 되기 때문. 따라서 bind를 활용해 강제 this 바인딩해야함.
- 화살표 함수 사용 (실행 시점이 아닌 작성 시점에 this가 상위 스코프로 결정)
- 렌더링 함수 내부에서 함수를 새롭게 만들어서 전달하는 방법
클래스 컴포넌트의 생명주기 메서드
생명주기는 크게 3가지이다.
- mount : 컴포넌트가 마운팅(생성)되는 시점
- update : 이미 생성된 컴포넌트의 내용이 변경(update)되는 시점
- unmount : 컴포넌트가 더 이상 존재하지 않는 시점
render()
- 생명주기 메서드 중 하나
- 리액트 클래스 컴포넌트의 유일한 필수 값으로 항상 쓰임
- 컴포넌트 UI 렌더링에 쓰임
- 마운트와 업데이트 과정에서 일어남
- 항상 순수해야 하며 no side-effects 여야한다. (같은 입력값에 항상 같은 결과)
componentDidMount()
- 컴포넌트 마운트 이후 준비되는 즉시 실행
- render()와는 다르게 this.setState()로 state 값 변경 가능
- UI 업데이트 이전에 렌더링을 다시 하기 때문에 사용자가 변경되는 것을 눈치 못챔
- setState는 기본적으로 생성자에서 하는 것이 좋음.
- 생성자 함수에서 할 수 없는 것, API 호출 후 업데이트, DOM에 의존적인 작업(이벤트 리스너 추가 등)에서만 사용 권장
componentDidUpdate()
- 컴포넌트 업데이트 이후 실행
- state, props의 변화에 따라 DOM 업데이트
- this.setState 사용 가능, 허나 무한 루프 발생가능
componentWillUnmount()
- 언마운트 or 더 이상 사용되지 않기 직전에 호출
- 클린업 함수 호출하기 위한 최적의 위치
- this.setState 호출 불가
shouldComponentUpdate()
- state나 props의 변경으로 리액트 컴포넌트가 리렌더링 되는 것을 막을 때 사용
- 컴포넌트에 영향 받지 않는 변화 정의 가능
- 특정 성능 최적화 상황에서만 고려
ex)
shouldComponentUpdate(nextProps: Props, nextState: State) {
return this.props.title !== nextProps.title || this.state.input !== nextState.input
}
static getDerivedStateFromProps()
- 가장 최근 도입된 생명주기 메서드
- 리액트 훅으로 구현 x
- 이전에 있었으나 이제는 사라진 componentWillReceiveProps를 대체할 수 있는 메서드
- render() 호출 직전 호출
- static이기에 this로 접근 불가능
- 여기서 반환하는 객체는 해당 객체의 내용이 모두 state로 들어가게 됨
- 모든 render() 실행 시 호출 됨
getSnapShotBeforeUpdate()
- 가장 최근 도입된 생명주기 메서드
- 리액트 훅으로 구현 x
- componentWillUpdate()를 대체할 수 있는 메서드
- DOM 업데이트 직전 호출
- componentDidUpdate로 전달
- DOM 렌더링 전 윈도우 크기 조절, 스크롤 위치 조정 등의 작업을 처리하는 데 유용
지금까지의 생명주기 메서드 정리
getDerivedStateFromError()
- 에러 상황에서 실행되는 메서드
- 리액트 훅으로 구현 x
- 에러 처리 로직 구현 가능
- render 단계에서 실행
componentDidCatch
- 자식 컴포넌트 에러 발생 시 실행
- getDerivedStateFromError에서 에러를 잡고 satte 결정 이후 실행
- 인수 두 개를 받는데 첫 번째는 getDerivedStateFromError와 동일한 error
- 두 번째는 어떤 컴포넌트가 에러를 발생시켰는지 정보를 가지고 있는 info
- 커밋 단계에서 실행되며, 부수 효과 수행 가능
- 개발 모드에서는 window까지 전파, 프로덕션 모드에서는 componentDidCatch로 잡히지 않은 에러만 window까지 전파.
클래스 컴포넌트의 문제점
- 데이터 흐름 추적이 어렵다.
- 애플리케이션 내부 로직의 재사용이 어렵다.
- 기능이 많아질수록 컴포넌트의 크기가 커진다.
- 클래스는 함수에 비해 상대적으로 어렵다.
- 코드 크기 최적화가 어렵다.