리액트 컴포넌트에는 생명주기(Life Cycle)가 존재한다. 이는 컴포넌트가 브라우저에 나타나고 업데이트 되고, 사라지게 될 떄 호출되는 메서드
다.
즉 특정 시점에 코드가 실행되도록 설정할 수 있다는 것이다.
컴포넌트 라이프 사이클은 크게
Mount
, Update
, UnMount
3가지로 분류된다.
Mount
: Dom 객체가 생성되고 브라우저에 나타나는 것을 의미한다.
Update
: 컴포넌트가 업데이트 될 때 실행되는 함수들을 보면 컴포넌트가 업데이트되는 경우는 props 값 변경, state 값 변경, 부모 컴포넌트가 리렌더링 될 때, this.forceUpdate로 강제로 리렌더링 되는 경우가 있다.
UnMount
: 컴포넌트가 DOM에서 제거되는 것을 언마운트(UnMount)라고 한다. 호출되는 함수는 하나로 componentWillUnmount 함수이다. 해당 컴포넌트가 제거되기 직전에 호출된다.
메서드를 간략히 나열하자면
render
componentDidMount
componentDidUpdate
componentWillUnmount
did가 붙은 함수는 어떤 작업을 처리한 후에 호출되는 함수이고, 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 를 사용학 되면 불필요한 리렌더나 무한루프를 일으키게 되고 성능면에서도 비효율적이다.