[React] useEffect

누리·2022년 10월 8일
0

React Foundation

목록 보기
7/18

React 에서 Side Effect의 올바른 발생 시점은 언제일까?

함수의 return문 위에서 바로 하고 싶은 동작(Side Effect)를 실행시킨다
1. 문제점

  • Side Effect가 렌더링을 blocking
    코드는 위에서 아래방향으로 순차적으로 발생한다 그래서 시간이 오래걸리는 Side Effect를 실행하면 JSX를 리턴하는 코드로 넘어가지 않는다. 이는 UI가 업데이트되기 까지 시간이 걸린다는 뜻이다
  • 매 렌더링마다 Side Effect가 발생
    특정 Side Effect는 매번 수행될 필요가 없는데 리액트에서 함수 컴포넌트의 리렌더링은 함수 컴포넌트를 다시 호출하는 방식으로 실행된다. Data Fetching 등 특정한 상황에서만 필요한 Side Effect가 매 렌더링마다(매 함수 호출마다) 무조건 발생하게 된다면 이는 비효율적인 프로그램이 되는 것이다

React에서 Side Effect를 발생시키기 위한 조건

  • 렌더링을 Blocking 하지 않기 위해서 렌더링이 모두 완료되고 난 후에 실행할 수 있어야 함
  • 매 렌더링마다 실행되는 것이 아니라 원할때만 조건부로 실행할 수 있어야 함

useEffect hook 사용

useEffect(콜백함수) : 하나의 함수를 인자로 받고 useEffect의 동작은 렌더링이 완료된 후 (JSX return 후)에, 콜백함수를 실행한다
1. Side Effect가 렌더링을 blocking하는 문제 해결

import { useEffect } from 'react';

const App = () => {
  console.log("side effect")

  useEffect(() => {
    console.log("side effect with useEffect")
  });
  console.log("render");
  return <h1>Hello, Wecoder</h1>;
};

콘솔창에는 side effect > render > side effect with useEffect 순으로 출력된다.

  1. 매 렌더링마다 Side Effect가 발생 하는 문제 해결
import { useEffect, useState } from 'react';

const App = () => {
  const [count, setCount] = useState(0);
  const [text, setText] = useState("");
   
  //useEffect(() => {
  //  console.log("side effect with useEffect")
  //})
  useEffect(() => {
    console.log("count changed")
  }, [count]); //count의 값이 변했을 때만 이 콜백함수를 호출
  
  console.log("render");
  return (
    <div>
    	<div className="wrapper">
    		<button 
    			onClick={() => setCount(count+1)}>Up
			</button>
    	</div>
		<input 
			value={text} 
			onChange={(e) => setText(e.target.value)} 
        />
    </div>
  );
};

처음 콘솔창에 useEffect()를 사용했기 때문에 render > side effect with useEffect 가 출력된다
그러나 버튼을 클릭하거나 input에 값을 입력할 때 마다 콘솔창에 render > side effect with useEffect 가 출력된다. useState() 함수에서 값이 매번 바뀔때 마다 리렌더링 되기 때문

useEffect(콜백함수, 의존성 배열) 을 사용하면 리렌더링 문제가 해결된다

  • 의존성 배열을 전달 하면 useEffect 함수는 먼저 의존성 배열의 값을 검사한다
  • 의존성 배열에 들어가 있는 값이 이전 렌더링과 비교했을 때 동일한지를 검사
  • 첫번째 렌더링에서는 무조건 콜백함수를 호출
  • 두번째 렌더링부터는 의존성 배열의 값이 변했을때만 콜백함수를 호출한다
    처음 콘솔창에 render > count changed 출력
    버튼을 클릭하면 콘솔창에 render > count changed 한번 더 출력
    input값을 입력하면 render만 출력됨

처음 렌더링에서 한번만 실행되게 하는 방법 :
useEffect(() => {콜백함수}, [])
의존성 배열을 [] 빈 배열로 입력해 두면 콜백함수가 첫 렌더링에서만 한 번 실행된다

Effect & Rendering Cycle

1 [mount] 컴포넌트가 렌더링 된다

  • 최초로 진행되는 렌더링은 브라우저에 처음으로 이 컴포넌트가 보였다는 의미로 "mount"라고 표현

2 [Side Effect] useEffect 첫 번째 인자로 넘겨준 콜백함수가 호출됨

3 [update] 컴포넌트의 state 또는 props가 변경되었을 경우 리렌더링 발생

4 useEffect는 두 번째 인자로 넘겨준 의존성 배열을 확인

  • case1 : 만약 의존성 배열이 전달되지 않았거나 의존성 배열 내부의 값 중 이전 렌더링과 비교했을 때 변경된 값이 하나라도 있다면 첫번째 인자로 넘겨준 콜백함수가 호출됨 [Side Effect]
  • case2 :만약 의존성 배열 내부의 값 중 이전 렌더링과 비교했을 때 변경된 값이 없다면 콜백함수를 호출하지 않음

5 state 또는 props가 변경된다면 3~4과정을 반복

6 [unmount] 컴포넌트가 더 이상 필요없어지면 화면에서 사라짐

  • 컴포넌트가 브라우저 화면에서 사라졌다는 의미로 "unmount"라고 표현

Clean Up

effect를 정리하고 치우는 작업

어떤 이펙트를 치워야 할까 ?

남아 있는 이펙트를 한 번 정리를 해주고 치운 다음 새로운 이펙트를 발생 시키거나 또는 최종적으로 치워줄 필요가 있다

  1. clean up 이 필요없는 effect : 코드의 동작에 아무런 영향을 미치지 않는 effect

  2. clean up 이 필요한 effect : 지속적으로 구독을 하면서 남아있는 effect

  • 계속해서 불필요하게 동작하게 된다면 비효율적인 프로그램이 되는것이다

Clean Up 사용 방법

clean up을 하면서 동작하고 싶은 것을 함수 형태로 만들어서 return한다
다음 이펙트가 발생하기 전에 기존에 부착해둔 eventListener 를 제거

useEffect(() => {
  const button = document.getElementById('consoleButton');

  const printConsole = () => {
    console.log('button clicked');
  };

  button.addEventListener('click', printConsole);

  // side effect를 clean up 하기 위한 함수를 선언한다.
  const cleanUp = () => {
    button.removeEventListener('click', printConsole);
  };

  // clean up 함수를 return 한다.
  return cleanUp;
});
  • useEffect는 cleanUp 함수를 다음 이펙트가 실행되기 전에 한 번 호출 해준다
  • 원하는 동작을 cleanUp 함수에 넣어두면, 다음 이펙트가 실행되기 전에 이전 이펙트를 정리할 수 있다
  • 아래와 같이 return 문에서 바로 적용 시킬 수도 있다
  return () => {
    button.removeEventListener('click', printConsole);
  };
  • 또한 컴포넌트가 언마운트 될 때도 cleanUp 함수를 호출한다 (페이지 이동시에 계속 구독되는 함수에 cleanUp을 적용한다)
profile
프론트엔드 개발자

0개의 댓글