클래스 컴포넌트의 생성부터 소멸까지 이르는 일련의 과정인 Life Cycle을 배우다.
1. Life Cycle
(그림 1) Life Cycle은 React의 클래스 컴포넌트의 생성부터 소멸까지 이르는 일련의 과정이다. 이것을 활용해서 컴포넌트가 생성되는 각 과정에서 실행되는 함수를 이용해서 내가 원하는 시점에서 원하는 동작을 하도록 구현이 가능하다.
(그림 2) Life Cycle의 과정은 3단계가 있다. 컴포넌트가 생성되는, 컴포넌트가 화면에 처음 그려지는 상태인 Mounting, 컴포넌트가 생성되고 난 후 props, state, force Update의 변화가 생겨서 렌더링, 리렌더링 될 때, 부모 컴포넌트가 갱신이 되었을 때를 가리키는 Updating, 컴포넌트가 화면에서 사라진 상태인 UnMount로 나눌 수 있다.
2. 클래스 컴포넌트
우리는 여태까지 컴포넌트를 함수형으로만 만들었다. 그렇다면 클래스 컴포넌트는 무엇일까?
# rcc 자동완성
import React, { Component } from 'react'
export default class RCCcomponent2 extends Component {
// 생성자 : state, 변수, 함수 값들을 초기화할 때 사용
// 화면이 렌더링 되기 전 실행
constructor(){
// state 초기화
super();
this.state = {num : 0}
// 실행할 함수 연결
this.handleIncrement = this.handleIncrement.bind(this)
console.log("1. constructor - 컴포넌트 생성");
}
render() {
return (
<div>RCCcomponent2</div>
)
}
}
render() 이전에는 자바스크립트 코드를 작성하는 곳이다. 함수형 컴포넌트와 다른 점은 state, 변수, 값들을 초기화할 때, 사용할 함수를 연결(바인딩)할 때 생성자인 constructor()를 사용해야 한다는 것이다. 그리고 constructor 안에서 super()를 작성해야 버그가 안 생긴다. super()는 부모 클래스의 생성자의 참조이다. 부모 생성자의 state나 props를 가져와서 사용할 때는 constructor(props), super(props)로 하고 사용한다. 클래스 컴포넌트에서 state를 생성할 때 this.state = {초기값}으로 만든다. super이후에 this를 사용할 수 있다.
handleIncrement(){
this.setState({
num : this.state.num + 1
})
}
위 함수가 실행 될 때 state 값을 변경하려면 함수형 컴포넌트에선 setState함수를 사용하기만 하면 됐다. 하지만 클래스 컴포넌트에선 this를 붙이고 그 안에서 객체형태로 값을 변화 시킨다. 그리고 저 함수가 특정 버튼을 누를 때 실행되어야 할 때 생성자인 constructor() 안에서 bind해서 연결해야한다. (// 실행할 함수 연결)
3. 클래스 컴포넌트의 Life Cycle 함수 및 확인
import React, { Component } from 'react'
export default class RCComponent extends Component {
// 생성자 : state, 변수, 함수 값들을 초기화할 때 사용
// 화면이 렌더링 되기 전 실행
constructor(){
// state 초기화
super();
this.state = {num : 0}
// 실행할 함수 연결
this.handleIncrement = this.handleIncrement.bind(this)
console.log("1. constructor - 컴포넌트 생성");
}
handleIncrement(){
this.setState({
num : this.state.num + 1
})
}
// Mount 생명주기라 연결 안해도 됨
componentDidMount(){
console.log("3. Mount 화면 첫 렌더링");
}
// Update
componentDidUpdate(){
console.log("화면 업데이트");
}
render() {
console.log("2. render - 화면이 렌더링 되는 중");
return (
<div>
RCComponent : {this.state.num}<br/>
<button onClick={this.handleIncrement}>+1</button>
</div>
)
}
}
클래스 컴포넌트의 Life Cycle 과정을 직접 console로 찍어서 과정을 확인해보겠다. 컴포넌트가 화면이 보이기 전에 생성자의 코드가 가장 먼저 실행되는 것을 볼 수 있다. 그리고 render 함수의 실행 로직이 실행되면서 화면에 띄워질 HTML 태그와 JS가 렌더링 되는 중인 것을 볼 수 있다. 그리고 최종적으로 Mount되는데, Mount되는 것을 componentDidMount 함수로 감지할 수 있다. 이 함수는 Life Cycle 함수이기 때문에 생성자 내부에서 연결하지 않아도 사용할 수 있다. 주로 페이지에 API를 가져와서 사용할 때 사용한다.
그럼 Update 과정을 감지하려면 state의 변화를주어야 한다. 버튼을 눌러서 state의 값을 변화시켜서 리렌더링을 하면서 Updating되는 것을 감지하는 componentDidUpdate 함수를 사용해서 감지하면 된다.
버튼을 누른 결과 state값이 변화해서 리렌더링 되면서 render함수가 다시 실행 되고난 후 componentDidUpdate 함수의 실행 로직이 실행되는 것을 볼 수 있다. 실행 순서들을 인지하고 사용자가 특정 시점에서 실행 시키고싶은 실행로직을 작성하면 된다.
- mount → componentDidMount
- Update → componentDidUpdate
- Unmount → componentWillUnmount
4. 함수형 컴포넌트에서의 생명주기 (useEffect)
import React, { useEffect, useState } from 'react'
/*
1. Life Cycle(생명주기)
- React 컴포넌트의 생성부터 소멸까지 이르는 일련의 과정
- Mount, Update, UnMount
Mount : 컴포넌트가 화면에 처음 그려지는 상태 EX) API를 가져와서 사용할 때
Update : props, state, force Update 변화가 발생했을 때 리렌더링 되는 상태
+ 부모 컴포넌트가 갱신되었을 때도 동작
EX) 특정 state가 변경되었을 때 활용
UnMount : 컴포넌트가 화면에서 사라진 상태
- 함수형 컴포넌트에서는 useEffect() 사용 (클래스 컴포넌트에선 사용 불가)
*/
const RFComponent = () => {
console.log("1. constructor 대체 => 함수 초기화");
const [num, setNum] = useState(0)
const [num2, setNum2] = useState(0)
const handleIncrement = () => {
setNum(num + 1)
}
const handleIncrement2 = () => {
setNum2(num2 + 1)
}
// 3. componentDidMount 대체 did와 update를 구분할 수 있음 => 배열의 역할
// useEffect(() => {실행 로직}, []) : 화면이 처음 렌더링 될 때 실행
useEffect(() => {
console.log("3. componentDidMount 대체 => 화면 렌더링 완료!");
}, []) // 배열은 빈배열이라면 생략 가능
useEffect(() => {
console.log("num or num2 state 변화 감지");
}, [num, num2])
useEffect(() => {
return () => {
console.log("Unmount 감지");
}
}, [])
// componentDidUpdate 대체 => 배열안에 state를 넣어주기
// useEffect(() => {실행 로직}, [변화를 감지 할 state])
// - 특정 state가 변화할 때 실행 : 배열안에 들어있는 state가 변할 때만 실행
// useEffect(() => {
// console.log("componentDidUpdate 대체 => [state] 배열안에 state 넣어주기");
// }, [num])
// useEffect(() => {
// console.log("num2 state 변화 감지");
// }, [num2])
return (
<div>
{console.log("2. render 대체 => return문 안")}
<p>
RFComponent : {num} <br/>
<button onClick={handleIncrement}>+1</button>
</p>
<p>
RFComponent : {num2} <br/>
<button onClick={handleIncrement2}>+1</button>
</p>
</div>
)
}
export default RFComponent
우리가 주로 사용하던 함수형 컴포넌트에서는 생명주기와 같은 역할을 하는 기능은 useEffect가 있다. reack Hook에서 지원하는 useEffect를 사용해서 mount와 Update, Unmount를 감지할 수 있다. useEffect의 문법은 다음과 같다.
useEffect(() => {
console.log("3. componentDidMount 대체 => 화면 렌더링 완료!");
}, []) // 배열은 빈배열이라면 생략 가능
useEffect(() => {
console.log("num or num2 state 변화 감지");
}, [num, num2])
useEffect(() => {
return () => {
console.log("Unmount 감지");
}
}, [])
useEffect 함수는 매개변수로 함수와 배열을 받는다. 배열에 아무것도 들어있지 않으면 Mount를 감지한다. 그리고 빈 배열이라면 매개변수에서 생략해도 된다. 빈 배열을 넣어야지 무한 루프 현상을 예방할 수 있다. 배열에는 state와 같이 변경되는 변수들이 들어갈 수 있다. 배열에 state가 들어가면 해당 state가 변화될 때만 Update를 감지한다. 예를들어 state1과 state2가 있는데 state1만 배열에 들어가 있는 상황에서 state2의 변화를 주었을 때는 useEffect 내부 함수가 실행되지 않는다.
함수형 컴포넌트 실행 순서는 return문 외부 -> return문 내부(렌더링) -> useEffect mount감지 순서이다.
'Front-End > React.js' 카테고리의 다른 글
[React.js] React Redux (0) | 2023.09.13 |
---|---|
[React.js] react-router (useParams, useSearchParams) (2) | 2023.09.12 |
[React.js] Context (0) | 2023.09.03 |
[React.js] useRef 설명 및 실습 (0) | 2023.08.30 |
[React.js] Filter 함수 (0) | 2023.08.29 |