● 라이프사이클 메서드의 이해
라이프사이클은 크게 세 가지, 마운트, 업데이트, 언마운트 카테고리로 나눕니다. 우선 어떤 것들이 있는지 간단히 알아보고 큰 흐름을 이해한 후 하나씩 살펴보는 것으로 하자.
- 마운트 : DOM이 생성되고, 브라우저 사엥 나타나는 것을 마운트라고 한다.
- constructor
- getDerivedStateFromProps (derived from: ~에서 파생되다.)
- render
- componentDidMount
- 업데이트 : 컴포넌트는 다음과 같이 4가지 경우에 업데이트된다.
- props가 바뀔 때, state가 바뀔 때, 부모 컴포넌트가 리렌더링될 때, this.forceUpdate로 강제로 렌더링 될 때
- getDerivedStateFromProps
- shouldComponentUpdate
- render
- getSnapshotBeforeUpdate
- componenetDidUpdate
- 언마운트 : 마운트의 반대 과정, 컴포넌트를 DOM에서 제거하는 것을 언마운트라고 한다.
- componentWillUnmount : 컴포넌트가 브라우저에서 사라기지 전에 호출하는 메서드
● 라이프사이클 메서드의 이해
⚬ render
라이프사이클 메서드 중 유일하게 필수 메서드입니다. DOM에 직접 접근해서도 안되고, setState도 사용해서는 안됩니다.
⚬ constructor
컴포넌트 생성자 메서드로, 처음에 실행된다. 여기서는 초기 state를 설정할 수 있습니다.
⚬ getDerivedStateFromProps 메서드 (nextProps, prevState)
부모 컴포넌트에서 props를 받아와 state에 동기화시키는 용도로 사용하며, 컴포넌트가 마운트될 때와 업데이트될 때 호출됩니다.
⚬ componentDidMount 메서드
첫 렌더링을 마친 후 실행됩니다. 여기서는 setTimout, setInterval, 네트워크 요청 같은 비동기 작업을 처리합니다.
⚬ shouldComponentUpdate 메서드 (nextProps, nextState)
이것은 props, state가 변경되었을 때 리렌더링을 시작할지 여부를 지정하는 메서드입니다. 메서드에서는 반드시 true 값 또는 false 값을 반환해야합니다. false값이 반환되면 리렌더링 되지 않습니다. 하지만 강제로 렌더링 되는 경우 (this.forceUpdate) 이 과정을 생략합니다.
⚬ getSnapshotBeforeUpdate 메서드 (prevProps, prevState)
render에 만들어질 결과물이 적용되기 전에 (즉, 컴포넌트 변화를 DOM에 적용하기 전에) 호출됩니다. 주로 업데이트하기 이전의 직전값을 참고할 일이 있을 때 활용됩니다.
⚬ componentDidUpdate 메서드 (prevProps, prevState, snapshot)
이것은 리렌더링을 완료 후 실행합니다. 업데이트가 끝난 직후 이므로 DOM 관련 처리를 해도 무방합니다. 또한 직전 값에 접근할 수 있씁니다.
⚬ componentWillUnmount 메서드
컴포넌트가 브라우저 상에서 없어질 때, DOM에서 제거할 때 실행합ㄴ디ㅏ.
⚬ componentDidCatch 메서드 (error, info)
컴포넌트 렌더링 도중에 에러가 발생했을 때 에러를 보여줍니다.
● 라이프사이클 메서드 사용하기
⚬ 예제 컴포넌트
App.js
import { Component } from "react";
import LifeCycleSample from "./LifeCycleSample";
function getRandomColor() {
return "#" + Math.floor(Math.random() * 16777215).toString(16);
}
class App extends Component {
state = {
color: "#000000",
};
handleClick = () => {
this.setState({
color: getRandomColor(),
});
};
render() {
return (
<div>
<button onClick={this.handleClick}>랜덤 색상</button>
<LifeCycleSample color={this.state.color} />
</div>
);
}
}
export default App;
LifeCycleSample.js
import { Component } from "react";
class LifeCycleSample extends Component {
state = {
number: 0,
color: null,
};
myRef = null;
constructor(props) {
super(props);
console.log("constructor가 실행되었습니다.");
}
static getDerivedStateFromProps(nextProps, prevState) {
console.log("getDerivedStateFromProps가 실행되었습니다.");
console.log(nextProps, prevState);
if (nextProps.color !== prevState.color) {
return { color: nextProps.color };
}
return null;
}
componentDidMount() {
console.log("componenetDidMount가 실행되었습니다. 한번만!");
}
shouldComponentUpdate(nextProps, nextState) {
console.log(
"shouldComponentUpdate가 실행되었씁니다. nextProps와 nextStates는"
);
console.log(nextProps, nextState);
return nextState.number % 10 !== 4;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate가 실행되었습니다.");
if (prevProps.color !== this.props.color) {
return this.myRef.style.color;
} else return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("componentDidUpdate가 실행되었습니다.", prevProps, prevState);
if (snapshot) {
console.log("업데이트되기전 색상", snapshot);
}
}
handleClick = () => {
this.setState({ number: this.state.number + 1 });
};
render() {
console.log("render가 실행됨");
const style = {
color: this.props.color,
};
return (
<div>
<h1 style={style} ref={(ref) => (this.myRef = ref)}>
{this.state.number}
</h1>
<p>color: {this.state.color}</p>
<button onClick={this.handleClick}>더하기</button>
</div>
);
}
}
export default LifeCycleSample;
처음에 마운트됐을 때에는
- constructor가 실행되고,
- getDerivedStateFromProp가 실행되고
- nextProps의 모습을 콘솔에 보여주고
- prevState의 모습을 보여준다.
- render함수가 실행되는 것을 볼 수 있다.
- render가 끝난뒤, componentDidMount 메서드가 실행된다.
props가 변했을 때에는
componenetDidMount 아래부터
- getDerivedStateFromProps가 실행된다.
- nextProps가 보여지고
- prevState가 보여진다.
- shouldComponentUpdate가 실행된다.
- nexProp와 nextState가 보여진다.
- render가 실행된다.
- getSnapShotBeforeUpdate가 실행된다.
- componentDidUpdate가 실행된다. 이전 props, 이전 state가 출력된다.
- snapshot이 출력된다.
LifeCycleSample의 state들이 변하면
- getDerivedStateFromProps가 실행된다.
- 다음 props와 이전 state를 보여준다. 즉 props에는 변화가 없으니 state 변경사항이다.
- shouldComponentUpdate가 실행된다.
- render가 실행된다.
- getSnapshotBeforeUpdate가 실행된다.
- componentDidUpdate가 실행된다.
- 스냅샷은 출력되지 않는 것을 볼 수 있다.
만약에 number 숫자가 4가 되었다면,
- shouldComponentUpdate 가 false가 되고,
- render, getSnapshotBeforeUpdate, componentDidUpdate 실행되지 않는다.
● 에러잡아내기
의도적으로 에러를 한번 발생시켜보자.
App.js
import { Component } from "react";
import LifeCycleSample from "./LifeCycleSample";
import ErrorBoundary from "./ErrorBoundary";
function getRandomColor() {
return "#" + Math.floor(Math.random() * 16777215).toString(16);
}
class App extends Component {
state = {
color: "#000000",
};
handleClick = () => {
this.setState({
color: getRandomColor(),
});
};
render() {
return (
<div>
<button onClick={this.handleClick}>랜덤 색상</button>
<ErrorBoundary>
<LifeCycleSample color={this.state.color} />
</ErrorBoundary>
</div>
);
}
}
export default App;
ErrorBoundary.js
import { Component } from "react";
class ErrorBoundary extends Component {
state = {
error: false,
};
componentDidCatch(error, info) {
this.setState({ error: true });
console.log({ error, info });
}
render() {
if (this.state.error) return <div>에러가발생햇읍니다.</div>;
return this.props.children;
}
}
export default ErrorBoundary;
만약 에러가 발생한다면, this.state.error을 true 값으로 만들어 <div>에러가 발생햇읍니다.</div>가 뜨게 만들었다.
그리고 콘솔창에 error, info가 뜨게 만들었다.
App.js에 <ErrorBoundary> 컴포넌트 안에, LifeCycleSample을 감싸주었다.
이렇게 뜨는 걸 볼 수 있다. 이처럼 에러가 발생한다면, 에러창을 만들어서 띄울 수도 있다.. ㅎ ㄷ ㄷ
● 라이프사이클 정리