
함수 컴포넌트는 0.14버전에서부터 존재하던 선언방식인데, 단 당시엔 무상태 컴포넌트 라서 그저 요소를 상태 관리없어 정적으로 랜더링하는 것이 목적이었다.
(별다른 생태 주기 메서드나 상태가 필요없이 render 만 하는 경우에만 사용)
훅이 등장해서야 많은 작업을 할 수 있었기에, 이전까지는 클래스 컴포넌트를 사용했다.
기본적으로 클래스 컴포넌트를 만들려면, 클래스 선언 후 extends로 만들고 싶은 컴포넌트를 상속받아야 한다.
React.Component/React.PureComponent
constructor (생성자)
컴포넌트가 초기화되는 시점에 호출된다.
여기에서 state 초기화를 할 수 있다.
super()를 호출해 부모 클래스(React.Component)의 생성자를 실행해야 한다.
- 이를 통해 React의 내부 기능을 사용할 수 있도록 초기 설정이 이루어진다.
ES2022의 클래스 필드(Class Fields) 문법 덕분에
constructor 없이도 state를 초기화할 수 있는데, state = { count: 1 }처럼 선언 가능하다.
ES2022 이상의 환경에서 지원되므로 구형 브라우저에서는
Babel의 @babel/plugin-proposal-class-properties 플러그인을 사용해 트랜스파일링해야 한다.
props (속성)
state (상태)
- 컴포넌트 내부에서 관리하는 값을 의미한다.
class SampleComponent extends React.Component {
state = { count: 0 };
render() {
return <div>{this.state.count}</div>;
}
}
```
메서드
undefined가 되는 문제를 방지하기 위해 bind()를 사용해야 한다. this가 결정되므로 bind()가 필요 없다. class SampleComponent extends React.Component {
state = { count: 0 };
// 1. Constructor에서 this 바인딩
// constructor(props) {
// super(props);
// this.handleClick = this.handleClick.bind(this);
// }
// 2. 화살표 함수 사용
handleClick = () => {
console.log(this.state.count);
};
render() {
return (
// 3. 렌더링 시 함수 생성 (지양)
// <button onClick={() => this.handleClick()}>Click</button>
// 2번 방식인 화살표 함수 사용
<button onClick={this.handleClick}>Click</button>
);
}
}
생명주기 메서드가 실행되는 시점
- 마운트 : 컴포넌트가 생성되는 시점
- 업데이트 : 이미 생성된 컴포넌트의 내용이 변경되는 시점
- 언마운트 : 컴포넌트가 더 이상 존재하지 않는 시점
render()
state 를 직접 변경하는 this.setState를 호출해서는 안된다. componentDidMount()
componentDidUpdate()
componentWillUnmount()
shouldComponentUpdate()
특정 조건에서만 리렌더링을 허용하여 성능을 최적화 가능!
this.setState() 호출 시 컴포넌트는 기본적으로 리렌더링을 트리거하는데, shouldComponentUpdate()에서 false를 반환하면 리렌더링을 방지할 수 있다.
shouldComponentUpdate(nextProps, nextState) {
// props의 title이 같지 않거나 state의 input이 같지 않은 경우에만 컴포넌트를 업데이트
return this.props.title !== nextProps.title || this.state.input !== nextState.input;
}
PureComponent 가 shouldComponentUpdate()를 사용할 수 있기에, Component와 랜더링에 있어 차이점을 갖는다.static getDerivedStateFromProps()
getSnapShotBeforeUpdate()
getDerivedStateFromError()
componentDidCatch()
error, 그리고 정확히 어떤 컴포넌트가 에러를 발생시켰는지 정보를 가지고 있는 info 를 받는다.getDerivedStateFromError() 와 componentDidCatch()는 ErrorBoundary, 에러 경계 컴포넌트를 만들기 위한 목적으로 많이 사용된다. 이를 통해 전역에서 처리되지 않은 에러를 처리하기 위한 용도로 사용된다.
다만, ErrorBooundary에서 주의할 점이 있다.
1. 개발모드와 프로덕션 모드에서 동작 차이
개발 모드에서는 에러가window까지 전파되지만, 프로덕션 모드에서는componentDidCatch()가잡지 못한 에러만 전파된다. (개발 모드에서 에러 처리가 중복될 수 있음)
2. componentDidCatch()의 두 번째 인수 errorInfo
errorInfo는 에러가 발생한 컴포넌트의 위치를 추적하는 정보인데,errorInfo.componentStack를 통해 어느 컴포넌트에서 에러가 발생했는지 알 수 있다.
이 정보는 Function.name 또는 displayName에 따라 결정되는데 익명함수(memo(() => { ... }))로 선언하면, 컴포넌트를 선언할 수 없어 정보가 제대로 표시되지 못한다.
데이터 흐름 추적 어려움
state가 업데이트됨 → 흐름 파악 어려움내부 로직 재사용 어려움
기능 증가 시 컴포넌트 크기 증가
클래스 문법의 복잡성
this 특성으로 인해 초보자에게 혼란 유발 코드 크기 최적화 어려움
핫 리로딩 불리
React.Component를 상속받아 componentDidMount, componentDidUpdate, componentWillUnmount 등의 생명주기 메서드 사용한다. useEffect 훅을 사용하여 비슷한 동작을 구현하지만, 생명주기 메서드와는 엄연하게 같다고는 할 수 없다. useEffect는 부수 효과 처리용으로 설계되었다.차이점
this.props를 사용하여 props를 가져오고, 컴포넌트의 인스턴스가 변할 때마다 업데이트된 값을 읽는다.왜 차이가 발생하는데?
this가 가리키는 객체가 변할 수 있으므로, 렌더링 중에 props 값이 바뀌면 변경된 값이 반영된다.this가 없고, props와 state가 함수의 인수로 들어오므로 그 시점의 값이 렌더링에 사용된다.해결 방법
this.props를 더 일찍 호출하여 값을 처리하거나, 함수의 인수로 넘겨줄 수 있다. (그치만, state과 props가 늘어날 수록 코드가 복잡해짐)클래스 컴포넌트는 당장 사라질 계획은 없고, 기존 코드가 많아 이를 모두 함수 컴포넌트로 바꾸는 건 쉽지 않다. 함수 컴포넌트로 전환하려면 충분한 경험과 실수 감지 시스템이 필요하고, 그렇지 않으면 굳이 바꿀 필요는 없다. 리액트를 처음 배우거나 새로운 프로젝트를 시작할 땐 함수 컴포넌트를 쓰는 게 좋다. 그치만 어느 정도 익숙해졌다면 클래스 컴포넌트도 공부할 필요가 있는데, 일부 에러 처리는 클래스 컴포넌트에서만 가능하기 때문이다.