
리액트 컴포넌트에는 생명주기(Life Cycle)가 존재한다. 이는 컴포넌트가 브라우저에 나타나고 업데이트 되고, 사라지게 될 떄 호출되는 메서드다.
즉 특정 시점에 코드가 실행되도록 설정할 수 있다는 것이다.
컴포넌트 라이프 사이클은 크게
Mount , Update , UnMount 3가지로 분류된다.
Mount : Dom 객체가 생성되고 브라우저에 나타나는 것을 의미한다.
Update: 컴포넌트가 업데이트 될 때 실행되는 함수들을 보면 컴포넌트가 업데이트되는 경우는 props 값 변경, state 값 변경, 부모 컴포넌트가 리렌더링 될 때, this.forceUpdate로 강제로 리렌더링 되는 경우가 있다.
UnMount: 컴포넌트가 DOM에서 제거되는 것을 언마운트(UnMount)라고 한다. 호출되는 함수는 하나로 componentWillUnmount 함수이다. 해당 컴포넌트가 제거되기 직전에 호출된다.
메서드를 간략히 나열하자면
rendercomponentDidMountcomponentDidUpdatecomponentWillUnmountdid가 붙은 함수는 어떤 작업을 처리한 후에 호출되는 함수이고, will 이 붙틑 함수는 어떤 작업을 처리하기 직전에 호출되는 함수인 것이다.
클래스 컴포넌트에서만 사용 가능했던 componentDidMout와 같은 생명주기 메서드들이 함수형 컴포넌트에서는
useEffect를 통해 함수형 컴포넌트의 라이프 사이클을 관리할 수 있다.
useEffect는 기본적으로
componentDidMount, componentDidUpdate, componentWillUnmount, getDerivedStateFromProps 의 역할을 모두 수행한다.
// 의존성 배열[]에 아무것도 넣지 않으면 Mount시에만 렌더해주고 끝난다. (1번만 실행)
useEffect (() =>{
console.log("마운트 됨");
}, [])
// 의존성 배열이 없기 때문에 뭐 하나라도 바뀌면 무조건 다시 실행된다.
useEffect(()=>{
console.log("수정하고 다시 그려짐")
})
// someState가 수정될때만 리렌더 해주기
useEffect(()=>{
console.log("수정하고 다시 그려짐")
},[someState])
useEffect(()=>{
console.log("수정하고 다시 그려짐")
//이부분이 끝나고 진행할 것들
return()=>{
console.log("여기서 나가기")
}
},[])
componentDidUpdate와 비슷하지만 다른점 하나는, Mount 되자마자 실행되는 점이다.
생명주기 메서드,훅 은 기본적으로 렌더(화면그리기) 이후에 실행된다.
따라서 useEffect와 lifecycle 메서드는 렌더 이후에 실행된다.
// 함수형 컴포넌트 useEffect 사용해서 생명주기 !!!
// 생명주기 메서드, 훅은 기본적으로 화면이 render 된 후에 실행횐다.
import { useRouter } from "next/router";
import { useState, useEffect, useRef } from "react";
// useRef 사용해서 input 태그 선택하고, focus() 기능 활용하여 커서를 깜빡이도록 한다.
interface IState {
count: number;
}
export default function CounterPage() {
const router = useRouter();
// inputRef = createRef<HTMLInputElement>();
const inputRef = useRef<HTMLInputElement>(null);
const [count, setCount] = useState(99);
/* 📌 1. DidMount */
// 1)
// componentDidMount() {
// console.log("마운트됨!!!");
// this.inputRef.current?.focus();
// 포커스 깜빡깜빡
// }
// 2)
// useEffect(() => {
// console.log("마운트됨!!!");
// inputRef.current?.focus();
// }, []);
// 의존성 배열[]에 아무것도 넣지 않으면 Mount시에만 렌더해주고 끝나게 됨
/* 📌 2. DidUpdate */
// 1)
// componentDidUpdate() {
// console.log("수정되고 다시그려짐!!!");
// }
// 2)
// useEffect(() => {
// console.log("수정되고 다시그려짐!!!");
// });
// 의존성 배열이 없기 때문에 뭐 하나라도 바뀌면 무조건 다시 실행
useEffect(() => {
console.log("수정되고 다시그려짐!!!");
}, [count]);
// count가 수정될때만 리렌더
/* 📌 3. WillUnmount */
// 1)
// componentWillUnmount() {
// console.log("컴포넌트 사라짐!!!");
// // 채팅방 나가기
// // api 요청!!!
// }
// 2)
// useEffect(() => {
// return () => {
// console.log("컴포넌트 사라짐!!!");
// };
// }, []);
/* 📌 4. DidMount와 WillUnmount를 합치기!! */
useEffect(() => {
console.log("마운트됨!!!");
inputRef.current?.focus();
return () => {
// 나갈때
console.log("컴포넌트 사라짐!!!");
};
}, []);
/* 📌 5. useEffect의 잘못된 사용 예(1. 추가렌더링, 2. 무한루프) */
// useEffect(() => {
// setCount((prev) => prev + 1);
// }, [count]);
const onClickCounter = () => {
// console.log(this);
// console.log("카운터 클릭!!!");
// console.log(this.state.count);
// this.setState((prev: IState) => ({
// count: prev.count + 1,
// }));
setCount((prev) => prev + 1);
};
const onClickMove = () => {
router.push("/");
};
console.log("나는 언제 실행되게?!!");
return (
<div>
<input type="text" ref={inputRef} />
<div>현재카운트: {count}</div>
<button onClick={onClickCounter}>카운트 올리기!!!</button>
<button onClick={onClickMove}>나가기!!!</button>
</div>
);
}
useEffecrt 내에서 setState를 사용할때는 정말 필요한 경우가 아니라면 지양하기.
컴포넌트가 마운트돈 이후 setState 를 적용하게 되면
state가 변경되고, 변경된 state로 컴포넌트가 다시 그려지게 된다.
여기서 useEffect 내에서 setState 를 사용학 되면 불필요한 리렌더나 무한루프를 일으키게 되고 성능면에서도 비효율적이다.