FrontEnd/React

[React] useEffect의 실행 시점

guineaa 2024. 7. 12. 15:41

useEffect는 리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 실행할 수 있도록 하는 Hook 이다.

useEffect는 component가 mount 됐을 때, unmount 됐을 때, update 됐을 때 특정 작업을 처리할 수 있다.

즉, 클래스형 컴포넌트에서 사용할 수 있었던 생명주기 메소드를 함수형 컴포넌트에서도 사용할 수 있게 된 것이다!

 

=> useEffect(effect function, dependency array);

 

1. mount 됐을 때 useEffect 실행 시점

useEffect는 컴포넌트가 mount, update, unmount '될 때' 실행 된다.

=> useEffect의 실행은 컴포넌트의 렌더링과 동시에 일어나는 것이 아니라 그 후에 일어난다.

 

❓useEffect의 mount 실행 시점을 확인하기 위해 다음과 같은 코드를 작성했다.

어떤 순서로 콘솔에 출력 될까?

import {useEffect} from 'react';

function App(){
	console.log('1');
    
    useEffect(()=>{
    	console.log('mount')
    },[]);
    
    console.log('2');
    
    return <div className='App'></div>;
}

1->2->mount 순으로 실행된다. 컴포넌트가 평가될 때 동시에 useEffect가 실행되는 것이 아니라,

컴포넌트의 렌더링이 끝난 후 useEffect가 실행된다는 것을 알 수 있다.

 

2. update, unmount 될 때 useEffect의 실행 시점

앞서서 컴포넌트가 렌더링 된 후에 useEffect가 실행되는 것을 보았다.

그렇다는 것은 update될 때 또한, 컴포넌트의 리렌더링이 완료된 후 useEffect가 실행될 것이라는 것 또한 생각해 볼 수 있다.

 

useEffect에 전달하는 콜백 함수도 return을 가질 수 있는데, 이 return에 함수를 전달할 수 있다.

이를 clean up 함수라고 한다.

cleanup 함수는 컴포넌트가 unmount 될 때 실행되는데, 이 cleanup함수의 실행 시점을 확인해 보자.

 

❓input을 하나 만들고, input의 state가 변경될 때마다 useEffect가 실행되도록 하였다.

어떤 순서로 실행이 될까...

import {useEffect, useState} from 'react';

function App(){
	const [input, setInput]=useState('');
    
    console.log('1');
    
    useEffect(()=>{
    	console.log('mount');
        return ()=>{console.log('unmount');}
    }, [input]);
    
    console.log('2');
    
    return (
    	<div className='App'>
        <input
        	value={input}
            onChange={(e)=>{
            	setInput(e.target.value);
            })></input>
        </div>
      );
 }

컴포넌트가 처음 렌더링 될 때, 앞서 본 바와 같이 1, 2, mount 순서로 실행된다.

그리고 input의 state가 업데이트 되어 컴포넌트가 리렌더링 되면 1,2, unmount, mount 순으로 실행된다.

컴포넌트가 재평가 되면서 1,2가 먼저 출력되고, 그 다음 unmount 되면서 cleanup 함수가 실행되어 unmount를 출력하고,

그 다음 update 되면서 mount를 출력한다.

즉, 컴포넌트는 리렌더링 될 때, 재평가 => 언마운트 => 업데이트 순으로 진행된다.

 

 

3. effect 함수가 mount, unmount가 각각 한 번만 실행되게 하려면 어떻게 해야 할까?

Effect 함수가 mount, unmount 시 단 한번만 실행되게 하고 싶으면,

의존성 배열에 빈 배열을 넣으면 된다.

useEffect(effect function, []);

의존성 배열을 생략하면 컴포넌트가 업데이트 될 때마다 호출된다!

import {useEffect, useState} from 'react';

function App(){
	const [input, setInput]=useState("");
    
    console.log('1');
    
    useEffect(()=>{
    	console.log('mount');
        return ()=>{console.log('unmount');}
    },[]);
    
    console.log('2');
    
    return(
    	<div className='App'>
        <input
        	value={input}
            onChange={(e)=>{setInput(e.target.value);}
        ></input>
        </div>
       );
 }

의존성 배열을 삭제했더니 unmount가 출력되지 않는다.

cleanup 함수가 실행되지 않은 것이다!

클린업 함수가 'unmount 될 때 실행된다'라는 말이 모호하다면, update 전에 실행된다고 생각하면 될 것 같다.

그래서 의존성 배열이 비어있어 update 될 때 useEffect가 실행되지 않는다고 생각하면, 클린업 함수도 실행되지 않는다.

useEffect의 클린업 함수는 unmount 시점에 호출되는 것이 아닌, 리렌더링이 발생했을 때의 의존성 배열에 변화가 있다면, 변화가 있을 당시 이전의 값을 기준으로 실행된다!

 

useEffect를 사용하며 발생하는 흔한 실수를 고치는 법

useEffect의 실수를 피하기 위해 몇 가지 감지하기 힘든 세부 사항이 있다.

useEffect에 종속성 배열을 전혀 제공하지 않고, 함수만 제공하면

함수는 컴포넌트가 렌더링 될 때마다 렌더링 이후에 실행된다.

이는 useEffect hook 내에서 state를 업데이트 하려고 할 때 문제를 일으킬 수 있다.

useEffect에서 state를 업데이트 하는 경우, React의 기본 동작은 컴포넌트를 다시 렌더링 하는 것 이다.

따라서 useEffect는 종속성 배열이 없을 때 모든 단일 렌더링 후에 실행 되므로 무한 루프가 발생한다!

// BEFORE
function MyComponent(){
	const [data, setData]=useState([]);
    
    useEffect(()=>{
		fetchData().then(myData=>setData(myData))
        //Error! useEffect runs after every render without the dependencies array, causing infinite loop
    });
}

첫번째 렌더링 후 useEffect가 실행되고, state가 업데이트 되어 다시 렌더링 되어 useEffect가 다시 실행되어 프로세스가 무한대로 다시 시작된다.

이것을 무한 루프(inifinite loop)라고 하며 이는 애플리케이션을 중단 시킨다.

useEffect내에서 state를 업데이트 하는 경우 빈 종속성 배열을 제공해야 한다.

useEffect에 빈 배열을 제공하면 컴포넌트가 처음 렌더링 된 후 콜백 함수가 1번만 실행된다.

 

이에 대한 일반적인 예는 데이터를 가져오는 것이다.

컴포넌트의 경우, 데이터를 한 번만 가져와서 상태에 넣은 다음 JSX에 표시할 수 있다.

// AFTER
function MyComponent(){
	const [data, setData]=useState([])
    
    useEffect(()=>{
    	fetchData().then(myData=>setData(myData))
        //Correct! Runs once after render with empty array
   },[]);
   
   return <ul>{data.map(item=> <li key={item}>{item}</li>)}</ul>
}

 

[React] useEffect 무한 루프 해결하기, cleanup function (tistory.com)

[React] useEffect 실행 시점 짚고 가기 (tistory.com)

'FrontEnd > React' 카테고리의 다른 글

[React] Side Effect와 useEffect  (1) 2024.07.11
[React] useState란?  (0) 2024.07.08