리액트의 라이프사이클(LifeCycle)
리액트의 각 컴포넌트는 생성되고 사라지는 일련의 생애주기(LifeCycle)를 가집니다.
단, 라이프 사이클은 리액트의 컴포넌트 중 클래스형 컴포넌트에만 적용 가능합니다.
함수형 컴포넌트는 Hook을 통해 얼추 이 라이프 사이클을 흉내낼 수는 있지만, 기본적으로 라이프 사이클은 클래스형 컴포넌트에서 사용 가능합니다.
리액트의 라이프사이클 흐름도(Life Cycle’s Flow)
리액트는 크게 마운트 - 업데이트 - 언마운트 의 흐름을 따른다고 설명합니다.
마운트는 리액트 컴포넌트가 화면에 등장할 때 한 번 동작하는 단계로, constructor 등으로 state를 설정하는 등의 작업을 하는 단계입니다.
업데이트는 컴포넌트의 input 상자에 값이 바뀐다거나, 클릭 이벤트로 리렌더링 하는 등의 경우에 거치는 단계로, 컴포넌트가 화면에서 완전히 사라진 것이 아니면 데이터가 바뀌거나 입력값을 받는 등 거의 대부분의 상호 작용에 대응할 수 있기 때문에 리액트의 컴포넌트 생애 주기 중에서 가장 많은 시간을 점유하는 단계 입니다.
언마운트는 리액트 컴포넌트가 화면에서 사라질 경우 거치는 단계로 이 단계를 거치면 리액트 컴포넌트의 라이프 사이클은 종료 됩니다.
마운트(Mount, Mounting)
마운트 단계의 메서드
constructor
constructor(props){
super(props);
console.log('constructor');
}
생성자 메서드
초기 state의 값을 설정 해주는 데 사용합니다.
render
render() {
console.log('render');
const style = {
color:this.props.color,
}
return(
<>
<div className="sample">
</div>
</>
)
}
리액트 라이프 사이클의 모든 메서드 중에서 유일하게 필수 메서드 입니다.
이 메서드 안에서는 이벤트 설정이 아닌 곳에서 setState를 사용하면 안됩니다.
브라우저에 DOM에도 접근 할 수 없습니다.
DOM 정보를 가져오거나 state에 변화를 주고 싶을 때는 componentDidMount 에서 처리해야 합니다.
getDerivedSateFromProps
static getDerivedStateFromProps(nextProps, prevProps){
console.log("getDerivedSateFromProps");
if(nextProps.color !== prevProps.color){
return {color:nextProps.color}
}
return null;
}
리액트 v16.3 이후 도입된 기능입니다.
props로 받아온 값을 state에 동기화 시키는 용도로 사용됩니다.
componentDidMount
componentDidMount(){
console.log('componentDidMount');
}
컴포넌트를 만들고 첫 렌더링을 다 마친 후의 상태입니다.
다른 자바스크립트 라이브러리, 프레임워크 함수, 이벤트 등록, setTimeout, setInterval, 네트워크 요청 같은 비동기 작업을 처리하는 데 쓰입니다.
업데이트(Update, Updation)
업데이트 단계의 메서드
shouldComponentUpdate(nextProps, nextState) { . . . }
shouldComponentUpdate(nextProps, nextState){
console.log('shouldComponentUpdate', nextProps, nextState);
return nextState.number % 10 !== 4; // 끝자리가 4일 때는 렌더링 안함.
}
props, state 변경 시 리렌더링 시작할 지 지정하는 메서드입니다.
반드시 boolean 값을 리턴 해야 하며, 리액트 페이지의 전체 오류가 발생(백지현상)합니다.
getSnapshotBeforUpdate(prevProps, prevState) { . . . }
getSnapshotBeforeUpdate(prevProps, prevState){
console.log('getSnapshotBeforeUpdate');
if(prevProps.color !== this.props.color){
return this.myRef.style.color;
}
return null;
}
리액트 v16.3. 이후 등장하였습니다.
render에서 만들어진 결과물이 실제 반영되기 직전에 호출합니다.
compoentDidUpdate 이전 메서드이므로 이 단계에서 리턴하는 값이 compoentDidUpdate 메서드의 snapshot 매개변수가 됩니다.
업데이트하기 직전의 값을 참고하는데 주로 사용되며 대표적으로 스크롤바 위치 유지 등에 응용됩니다.
componentDidUpdate(prevProps, prevState, snapshot) { . . . }
componentDidUpdate(prevProps, prevState, snapshot){
console.log('componentDidUpdate', prevProps, prevState, snapshot);
if(snapshot){
console.log('업데이트 직전의 색상 :', snapshot);
}
}
리렌더링 완료 후 실행합니다.
업데이트 직후이므로 DOM 관련 처리가 가능합니다.
prevProps또는 prevState에 접근하여 컴포넌트가 이전에 가졌던 데이터에 접근 가능합니다.
getSnapshotBeforUpdate 에서 리턴한 값이 있다면 snapshot 변수로 받아 사용 가능합니다.
언마운트(UnMount, UnMounting)
언마운트 단계의 메서드
componentWillUnmount
componentWillUnmount(){
console.log('componentWillUnmount');
}
컴포넌트를 DOM에서 제가할 때 실행합니다.
componentDidMount 에서 생성한 DOM, event 등이 있다면 제거해야 합니다.
에러 (Error)
컴포넌트의 일반적인 라이프 사이클은 아닙니다.
컴포넌트에서 오류를 감지하면 캐치하고 오류 페이지를 렌더링하는데 응용 가능합니다.
compoentDidCatch
컴포넌트의 예외 처리를 담당합니다.
실제 렌더링 되는 컴포넌트에서 해당 메서드를 사용하면 에러를 잡을 수 없습니다.
이 메서드를 사용하면 부모 컴포넌트에서 넘겨주는 children 프로퍼티를 통해 오류를 검출하므로, 예외 처리를 원하면 별도의 예외처리 담당 컴포넌트를 작성하고 실제 렌더링 될 컴포넌트를 감싸는 식으로 코드를 작성해야 합니다.
에러 핸들링 컴포넌트는 아래와 같이 작성할 수 있습니다.
import { Component } from "react";
class ErrorHandlingComp extends Component {
state = {
isError: false,
};
componentDidCatch(error, info) {
this.setState({
isError: true,
});
console.log({ error, info });
}
render() {
if (this.state.isError) return <div className="error-div">에러가 발생했습니다.</div>;
return this.props.children;
}
}
export default ErrorHandlingComp;
App.js (부모 컴포넌트) 에서 에러를 핸들링 하도록 작성합니다.
import './App.css';
import './asset/Custom.css';
import './asset/LifeCycleComp.css'
import {Fragment, Component} from "react";
import LifeCycleComp01 from "./components/LifeCycleComp01";
import ErrorHandlingComp from './components/ErrorHandlingComp';
function getRandomColor() {
return '#' + Math.floor(Math.random()*16777215).toString(16);
}
class App extends Component {
state = {
color :'#000000',
name : 'LifeCycle'
}
handleClick = () => {
this.setState({
color:getRandomColor()
});
}
render() {
const {name} = this.state;
return(
<>
<div className="main-header">
<p>React의 <span>{name}</span> 에 대해 알아보기</p>
<button onClick={this.handleClick}>랜덤 색상</button>
</div>
<ErrorHandlingComp>
<LifeCycleComp01 color={this.state.color} />
</ErrorHandlingComp>
</>
)
}
}
export default App;
클래스형 컴포넌트의 라이프사이클 적용 예시
import { Fragment, Component } from "react";
class LifeCycleComp01 extends Component{
state = {
number: 0,
color:null,
}
myRef = null; // ref를 설정하기 위한 부분
constructor(props){
super(props);
console.log('constructor');
}
static getDerivedStateFromProps(nextProps, prevProps){
console.log("getDerivedSateFromProps");
if(nextProps.color !== prevProps.color){
return {color:nextProps.color}
}
return null;
}
componentDidMount(){
console.log('componentDidMount');
}
shouldComponentUpdate(nextProps, nextState){
console.log('shouldComponentUpdate', nextProps, nextState);
return nextState.number % 10 !== 4; // 끝자리가 4일 때는 렌더링 안함.
}
componentWillUnmount(){
console.log('componentWillUnmount');
}
handleClick = () => {
this.setState({
number:this.state.number + 1,
});
}
getSnapshotBeforeUpdate(prevProps, prevState){
console.log('getSnapshotBeforeUpdate');
if(prevProps.color !== this.props.color){
return this.myRef.style.color;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot){
console.log('componentDidUpdate', prevProps, prevState, snapshot);
if(snapshot){
console.log('업데이트 직전의 색상 :', snapshot);
}
}
render() {
console.log('render');
const style = {
color:this.props.color,
}
return(
<>
<div className="comp class-comp life-comp">
<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>
</div>
</>
)
}
}
export default LifeCycleComp01;