React Native 클래스 컴포넌트 라이프사이클 API - with TypeScript
by Jo
클래스 컴포넌트는 함수형 컴포넌트와 달리 기본적으로 라이프 사이클 API 들이 있다.
간단한 예제를 통해 어떤 API 들이 있고, 각각 어떠한 역할을 하는지 알아볼 것이다.
다음은 인삿말, 이름을 Props 로 받아서 인삿말이 ‘Hello’ 이면 “Hello, World! count is {count}” 를 출력하고 아닌 경우엔 “{인삿말}, {이름}! count is {count}” 를 출력하는 예제이다.
텍스트를 터치하면 count 가 올라가며, count 가 5가 되면 터치를 해도 더이상 숫자가 올라가지 않는다.
import React from 'react';
import { Text, SafeAreaView, TouchableOpacity } from 'react-native';
// Props interface 정의
interface Props {
greetings?: string;
initName: string;
}
// State interface 정의
interface State {
name: string;
count: number;
error: Boolean;
}
// 클래스 컴포넌트
class HelloWorld extends React.Component<Props, State> {
// 클래스 생성자
constructor(props: Props) {
super(props);
console.log('constructor');
this.state = {
name: props.initName,
count: 0,
error: false,
};
}
// 화면 렌더링 함수
render() {
console.log('render');
const { greetings } = this.props;
const { name, count, error } = this.state;
return (
<SafeAreaView>
{!error && (
<>
<TouchableOpacity onPress={()=>{this.setState({ count: count + 1 })}}>
<Text>{greetings}, {name}! count is {count}</Text>
</TouchableOpacity>
</>
)}
</SafeAreaView>
);
}
// Props 와 State 동기화 함수
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
console.log('getDerivedStateFromProps');
if (nextProps.greetings === 'Hello') {
return { name: 'World' };
} else {
return null;
}
}
// 클래스 컴포넌트 마운트시 호출되는 함수
componentDidMount() {
console.log('componentDidMount');
}
// 리렌더링 여부 결정 함수
shouldComponentUpdate(nextProps: Props, nextState: State) {
console.log('shouldComponentUpdate');
if (nextState.count > 6) {
return false;
} else {
return true;
}
}
// render 함수 호출시 화면 갱신 직전에 호출되는 함수
getSnapshotBeforeUpdate(prevProps: Props, prevState: State) {
console.log('getSnapshotBeforeUpdate');
return {
testData: true,
};
}
// render 함수 호출시 getSnapshotBeforeUpdate 다음으로 호출되는 함수
componentDidUpdate(nextProps: Props, prevState: State, snapshot: null) {
console.log('componentDidUpdate');
return true;
}
// 해당 컴포넌트가 unmount 될 때 호출되는 함수
componentWillUnmount() {
console.log('componentWillUnmount');
}
// 컴포넌트 렌더링에서의 예외 처리 함수
componentDidCatch(error: Error, info: React.ErrorInfo) {
this.setState({
error: true,
});
}
}
export default HelloWorld;
1. constructor()
constructor(props: Props) {
super(props);
console.log('constructor');
this.state = {
name: props.initName,
count: 0,
error: false,
};
}
클래스 컴포넌트에서는 함수형과는 다르게 생성자가 존재한다. 다만 State 를 사용하지 않아서 초기값을 설정할 필요가 없다면 생략해도 된다.
생성자 함수에서는 항상 super(props) 를 사용하여 먼저 부모 컴포넌트인 React.Component 의 생성자를 호출해야 한다.
또한 생성자 함수에서만 this.state 를 사용하여 state 의 값을 직접 지정할 수 있고, 나머지 부분에서는 this.setState 를 사용하여야 한다.
2. render()
render() {
console.log('render');
const { greetings } = this.props;
const { name, count, error } = this.state;
return (
<SafeAreaView>
{!error && (
<>
<TouchableOpacity onPress={()=>{this.setState({ count: count + 1 })}}>
<Text>{greetings}, {name}! count is {count}</Text>
</TouchableOpacity>
</>
)}
</SafeAreaView>
);
}
render 함수는 클래스 컴포넌트가 렌더링 되는 부분을 정의한다.
화면에 컴포넌트를 렌더링 할 때 이 함수가 호출되어 반환값이 화면에 표시되게 된다.
render 함수는 부모로 부터 받은 Props 값이 변경되거나 this.setState 로 State 값이 변경되어 화면 갱신이 필요해지면 호출된다.
따라서 render 함수에서 this.setState 로 State 값을 변경할 경우 무한 루프에 빠질 수도 있으므로 주의해야 한다.
3. getDerivedStateFromProps()
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
console.log('getDerivedStateFromProps');
if (nextProps.greetings === 'Hello') {
return { name: 'World' };
} else {
return null;
}
}
gerDerivedStateFromProps 함수는 Props 와 State 를 동기화 할 때 사용된다.
부모로부터 받은 Props 값으로 State 값을 설정하거나, Props 값에 의존하여 State 값을 결정할 때 해당 함수에서 관련 로직을 짜주면 된다.
로직에 따라 결정된 값을 반환하면 State 값이 설정된다.
만약 동기화할 State 가 없을 경우나 생성자에서 설정한 값을 그대로 쓰고싶을 경우엔 null 을 반환하면 된다.
이 함수는 컴포넌트가 생성될 때 한번 호출되어 동기화를 진행하고, 이후 Props 값이 변경될 때마다 호출하여 Props 와 State 간 동기화를 수행한다.
이 예제에서는 Props 의 greetings 값이 만약 ‘Hello’ 일 경우 State 의 name 을 ‘World’ 로 바꾸도록 한다.
그 외의 경우에는 null 을 리턴하여 initName 으로 생성자에서 설정한 name 값이 유지되도록 한다.
4. componentDidMount()
componentDidMount() {
console.log('componentDidMount');
}
componentDidMount 함수는 클래스 컴포넌트가 마운트된 후, 즉 화면에 처음 표시된 후에 딱 한번만 호출된다.
따라서 ajax 를 통해 데이터를 읽어들이거나 다른 자바스크립트 라이브러리와 연동을 수행하기에 적합하다.
5. shouldComponentUpdate()
shouldComponentUpdate(nextProps: Props, nextState: State) {
console.log('shouldComponentUpdate');
if (nextState.count > 6) {
return false;
} else {
return true;
}
}
shouldComponentUpdate 함수를 이용하여 렌더링을 제어할 수 있다.
앞서 말한 바와 같이 클래스 컴포넌트는 Props 나 State 값이 변경될 경우 리렌더링 되는데, 이 함수를 사용하여 리렌더링 여부를 결정할 수가 있다.
이 함수에서 true 를 반환하면 리렌더링이 수행되고, false 를 반환하면 리렌더링을 하지 않는다.
렌더링은 리액트 컴포넌트 중에 가장 비용을 많이 차지하는 부분이기 때문에 이를 이용하여 꼭 필요한 경우에만 렌더링을 하도록 하여 앱을 최적화할 수 있다.
이 예제의 경우 State count 값이 6 이상이면 false 를 반환하여 더이상 리렌더링 하지 않도록 하였다.
따라서 아무리 텍스트를 눌러봐도 count 값은 5 이상으로 증가하지 않는것 처럼 보이게 된다.
6. getSnapshotBeforeUpdate()
getSnapshotBeforeUpdate(prevProps: Props, prevState: State) {
console.log('getSnapshotBeforeUpdate');
return {
testData: true,
};
}
getSnapshotBeforeUpdate 함수는 Props 또는 State 가 변경되어 화면을 다시 그리기 위해 render 함수가 호출된 후에 화면 갱신 직전에 호출된다.
이 함수의 반환 값은 다음으로 호출되는 componentDidUpdate 함수의 세 번째 매개변수인 snapshot 으로 전달된다.
잘 사용되지는 않으며, 주로 화면을 갱신하는 동안 수동으로 스크롤의 위치를 고정하는 경우 등에 사용한다고 한다.
7. componentDidUpdate()
componentDidUpdate(nextProps: Props, prevState: State, snapshot: null) {
console.log('componentDidUpdate');
return true;
}
componentDidUpdate 함수는 Props 또는 State 의 변경으로 화면 리렌더링 시 getSnapshotBeforeUpdate 함수 다음으로 호출된다.
컴포넌트가 처음 화면에 렌더링 될 때는 이 함수 대신 componentDidMount 함수가 호출되고, 그 이후로 렌더링이 발생할 때 componentDidMount 는 두 번 다시 호출되지 않고 componentDidUpdate 가 호출된다.
이 함수도 getSnapshotBeforeUpdate 와 마찬가지로 잘 사용되지는 않으며, getSnapshotBeforeUpdate 와 함께 사용해서 스크롤을 수동으로 고정시키거나 할 때 사용된다고 한다.
또한 render 와 마찬가지로 this.setState 를 이 안에서 호출했다가는 무한 루프에 빠질 수 있다.
8. componentWillUnmount()
componentWillUnmount() {
console.log('componentWillUnmount');
}
componentWillUnmount 함수는 component 가 화면에서 완전히 사라진 후 호출된다.
component 가 해제되는 과정에서 호출되기 때문에 보통 componentDidMount 에서 연동한 자바스크립트 라이브러리르 해제하거나 setTimeout, setInterval 등의 타이머를 각각 clearTimeout, clearInterval 로 해제할 때 사용된다.
이 함수는 컴포넌트가 화면상에서 사라진 후 호출되기 때문에 여기서 this.setState 를 호출하면 갱신하려는 컴포넌트가 사라진 상태에서 리렌더링을 시도하게 되므로 메모리 leak 등의 문제가 발생할 수 있다.
9. componentDidCatch()
componentDidCatch(error: Error, info: React.ErrorInfo) {
this.setState({
error: true,
});
}
componentDidCatch 함수는 컴포넌트 렌더링 시의 예외 처리를 해주는 함수이다.
render 함수의 return 부분에서 에러가 발생하면 componentDidCatch 함수가 실행된다.
이 예제의 경우 render 함수에서
{!error && (
<>
<TouchableOpacity onPress={()=>{this.setState({ count: count + 1 })}}>
<Text>{greetings}, {name}! count is {count}</Text>
</TouchableOpacity>
</>
)}
위와 같이 error 가 false 일 경우에만 출력하도록 해두었다.
componentDidCatch 함수가 호출되면 error 값이 true 로 바뀐 다음 리렌더링 되므로(this.setState 로 error 값을 변경하기 때문에 리렌더링아 수행된다) 문제가 발생한 경우 자식 컴포넌트를 표시하지 않음으로써 비정상 종료되는 것을 막을 수 있다.
라이프 사이클 API 호출 순서
-
컴포넌트 생성시: constructor -> getDerivedStateFromProps -> render -> componentDidMount
-
컴프넌트의 Props 변경시: getDerivedStateFromProps -> shouldComponentUpdate -> render -> getSnapshotBeforeUpdate -> componentDidUpdate
-
컴포넌트의 State 변경시: shouldComponentUpdate -> render -> getSnapshotBeforeUpdate -> componentDidUpdate
-
컴포넌트 렌더링 중 에러 발생시: componentDidCatch
-
컴포넌트 제거시: componentWillUnmount
Subscribe via RSS