React state와 LifeCycle
함수형 컴포넌트와 class형 컴포넌트의 LifeCycle이 어떻게 되는지 간단하게 비교해 보는 글이다.
클래스형 컴포넌트와 생명주기 메서드
1. Mount(컴포넌트가 처음 실행될 때)
- state, context, defalutProps 저장
- componentWillMount - 안전하지 않은 접근
- render
- componentDidMount - DOM 접근 가능
2. Props Update(프롭스가 업데이트될때)
- componentWillReceiveProps - 사용 종료
- shouldComponentUpdate
- componentWillUpdate - 사용 종료
- render
- componentDidUpdate - DOM 접근 가능
3. State Update (스테이트가 업데이트됐을 때)
- shouldComponentUpdate
- componentWillUpdate - 사용 종료
- render
- componentDidUpdate - DOM 접근 가능
사실상 componentWillReceiveProps와 componentWillUpdate의 사용 종료로 state와 props가 업데이트될 때 동일하게 작동한다.
4. Unmount (컴포넌트가 제거되는 것)
- componentWillUnmount
사용 종료 기준은 아래와 같다.
함수형 컴포넌트와 useEffect 훅
컴포넌트의 실행
- 함수형 컴포넌트 호출
- 함수형 컴포넌트의 내부에서 실행
- return()으로 화면에 렌더링
- 생명주기 메서드 대신 useEffect를 통한 비슷한 처리 가능
dependency에 따른 useEffect의 실행
- deps 값이 없는 경우 : 화면이 렌더링 된 이후 수행이 되며, 리렌더링이 발생할 때마다 다시 실행
- deps 값이 빈 배열인 경우 : 첫 렌더링 완료 후 1회만 실행
- deps 값이 존재하는 경우 : 첫 렌더링 완료 후 1회 실행 && deps 값이 변경되었을 경우마다 실행
// dep X
useEffect(() => {
// effect
return () => {
// cleanup
};
});
// dep []
useEffect(() => {
return () => {
};
}, []);
// dep [some dep...]
useEffect(() => {
return () => {
};
}, [dep]);
[Mounting] useEffect() - 컴포넌트 렌더링 이후 실행
- dep 설정에 따라 실행됨
[Updating] useEffect() - 컴포넌트 내에서 변화가 발생했을 경우 실행
- 부모 컴포넌트의 리렌더링, 부모로부터의 props값 변화, 해당 컴포넌트 내에서 state 변경 등
[Unmounting] useEffect() - 컴포넌트 내에서 DOM을 제거할 때 실행되는 메서드
- 컴포넌트의 DOM이 제거될 때 수행되며 useEffect 내부의 return 값이 사용됨
위와 같이 컴포넌트를 여닫는 페이지가 있다고 가정했을 때, console을 통해 대략적인 흐름을 확인해 볼 수 있다.
Class형 컴포넌트
컴포넌트가 처음 실행될 때
state가 변경될 때 (props가 변경될 때와 동일)
컴포넌트가 제거될 때
함수형 컴포넌트
컴포넌트가 처음 실행될 때
컴포넌트가 업데이트될 때
콘솔에 출력된 것과 같이, useEffect의 return 이후 부분은 componentWillUnmount와 비슷하지만, 컴포넌트가 다시 렌더링 되기 전마다 다시 실행된다.
컴포넌트가 제거될 때
컴포넌트가 제거될 때는 componentWillUnmount처럼 useEffect의 return 부분이 실행되는 것을 볼 수 있다.
사용된 코드
// Lifecycle.jsx
import { useState } from "react";
import { FunctionComponent } from "./FunctionComponent";
import { ClassComponent } from "./ClassComponent";
export const Lifecycle = () => {
const [fnOpen, setFnOpen] = useState(false);
const [classOpen, setClassOpen] = useState(false);
const effectComponentHandler = (num) => {
if (num) {
setFnOpen(!fnOpen);
} else {
setClassOpen(!classOpen);
}
};
return (
<div>
<p>Fn component / Class component</p>
<button onClick={() => effectComponentHandler(1)}>함수 open/close</button>
<button onClick={() => effectComponentHandler("")}>
클래스 open/close
</button>
{fnOpen ? <FunctionComponent some1={1} some2={2} some3={3} /> : null}
{classOpen ? <ClassComponent some1={1} some2={2} some3={3} /> : null}
</div>
);
};
// FunctionComponent.jsx
import { useState, useEffect } from "react";
export const FunctionComponent = (props) => {
console.log("컴포넌트 렌더링", props);
const [count, setCount] = useState(0);
useEffect(() => {
// effect
if (count === 0) {
console.log(`useEffect 첫 등장`);
} else {
console.log(`useEffect 다시 등장 count: ${count}`);
}
return () => {
// cleanup
console.log(
"useEffect 퇴장 --- componentWillUnmount와 비슷하지만 리렌더링마다 사용됨"
);
};
}, [count]);
const plus = () => {
setCount((prev) => prev + 1);
};
const minus = () => {
setCount((prev) => prev - 1);
};
return (
<div>
<p>함수형 컴포넌트</p>
<span>{count}</span>
<button onClick={plus}>+</button>
<button onClick={minus}>-</button>
<p>Props 받기</p>
<div>props1 = {props.some1}</div>
<div>props2 = {props.some2}</div>
<div>props3 = {props.some3}</div>
</div>
);
};
// ClassComponent.jsx
import { Component } from "react";
export class ClassComponent extends Component {
constructor(props) {
super(props);
// Class형 컴포넌트의 state는 무조건 객체 형태여야 한다.
this.state = {
counter: 0,
};
console.log(
"Mount - 컴포넌트가 처음 실행될 때 state, context, defalutProps 저장",
props
);
}
plus = () => {
this.setState((state) => ({ counter: state.counter + 1 }));
};
minus = () => {
this.setState((state) => ({ counter: state.counter - 1 }));
};
componentWillMount() {
console.log("componentWillMount");
}
componentDidMount() {
console.log("componentDidMount - DOM 접근 가능");
}
componentDidUpdate() {
console.log("componentDidUpdate");
}
shouldComponentUpdate() {
console.log("shouldComponentUpdate - state or props 업데이트");
return true;
}
componentWillUnmount() {
console.log("componentWillUnmount");
}
render() {
return (
<div>
<p>Class형 컴포넌트</p>
<span>{this.state.counter}</span>
<button onClick={this.plus}>+</button>
<button onClick={this.minus}>-</button>
<p>Props 받기</p>
<div>props1 = {this.props.some1}</div>
<div>props2 = {this.props.some2}</div>
<div>props3 = {this.props.some3}</div>
</div>
);
}
}
참조
https://ko.reactjs.org/docs/state-and-lifecycle.html
https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html
'개발일지 > React' 카테고리의 다른 글
React Form event type (feat. 타입 단언 as) (0) | 2023.04.13 |
---|---|
React/Javascript 부드러운 스크롤 이동 적용 (0) | 2023.03.25 |
React useCallback을 이용해 함수 재사용하기 (0) | 2023.03.16 |
React useMemo 사용하기 (0) | 2023.03.16 |
React.memo 사용하기 (0) | 2023.03.16 |
댓글