본문 바로가기
React

[React] useEffect

by NJ94 2023. 9. 21.

useEffect 

import { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    // Code here will run after *every* render
  });
  return <div />;
}

구성 요소가 렌더링될 때마다 React는 화면을 업데이트 한 다음 내부의 코드를 실행한다. 

 

React에서 렌더링은 JSX의 순수한 계산이어야 하며, DOM 수정과 같은 부작용을 포함해선 안된다.

렌더링 중에 DOM 노드로 작업을 시도하기 떄문에 아래의 코드는 문제가된다.

 

 

* JSX 순수한 계산 : 
https://react.dev/learn/keeping-components-pure 
: 순수 함수는 계산만 수행하며 그 이상은 수행하지 않는다. 구성 요소를 순수함수로만 작성하면 코드베이스가 커짐에 따라 당황스러운 버그와 예측할 수 없는 동작을 방지할 수 있다. 

 

 

1. 버튼 클릭

2.. isPlaying의 상태 변경으로 DOM 렌더링 시작,

3. DOM 렌더링 중, Video 실행됨. 

 

import { useRef, useState, useEffect } from 'react';
import './App.css'

type viedeoType = {
  src: string,
  isPlaying: boolean
};

function VideoPlayer({ src, isPlaying }: viedeoType) {
  const ref = useRef<HTMLVideoElement | null>(null);

  if(!ref.current) {
    return;
  }
  if (isPlaying) {
    ref.current.play();
  } else {
    ref.current.pause();
  }

  return <video ref={ref} src={src} loop playsInline />;
}

export default function App() {
  const [isPlaying, setIsPlaying] = useState(false);
  // 2. isPlaying 상태가 변경됨
  
  return (
    <>
      { /** 1. 사용자가 버튼을 클릭함. */ }
      <button onClick={() => setIsPlaying(!isPlaying)}> 
        {isPlaying ? 'Pause' : 'Play'}
      </button>
      <VideoPlayer
        isPlaying={isPlaying}        
        src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
      />
      { /** 3. isPLaying 상태가 변경됨에 동시에, Video의 DOM요소도 렌더링됨 */ }
    </>
  );
}

 

JSX의 순수한 동작을 하기 위해서 사용자가 버튼을 클릭 이후, DOM이 재렌더링 된 이후에 

Video 가 실행되도록 처리 해야한다.

 

useEffect(() => {
    if(!ref.current) {
      return;
    }
    if (isPlaying) {
      ref.current.play();
    } else {
      ref.current.pause();
    }
  });

useEffect의 사용은 조심해야한다. useEffect로 인하여 아래와 같은 무한루프를 발생시킬 수 도 있다.

 

1. 화면 렌더링

2. useEffect() 실행

3. setCount(count + 1) 실행

4. count의 상태가 변경되었기 때문에, useEffect() 실행

4. setCount(count  + 1) 실행

 

const [count, setCount] = useState(0);
useEffect(() => {
  setCount(count + 1);
});

useEffect 사용법

useEffect(() => {
  // 모든 렌더링 이후에 실행됨
});

useEffect(() => {
  // 렌더링 이후 한번만 실행됨
}, []);

useEffect(() => {
  // 렌더링 이후, ,a, b 의 값이 변경된 이후에도 실행됨
}, [a, b]);

 

위의 Video에 useEffect()는 모든 렌더링 이후에 실행되기떄문에 아래와 같이 코드를 수정해야한다.

 

//렌더링 이후에 무조건 실행됨
useEffect(() => {
    if(!ref.current) {
      return;
    }
    if (isPlaying) {
      ref.current.play();
    } else {
      ref.current.pause();
    }
});

//isPlaying 변경 이후에 실행됨
useEffect(() => {
    if(!ref.current) {
      return;
    }
    if (isPlaying) {
      ref.current.play();
      console.log('play..');
      
    } else {
      ref.current.pause();
      console.log('pause..');
      
    }
}), [isPlaying];

 

 


이벤트 구독

 

useEffect 내부에 특정 요소에 이벤트를 주었다면, useEffect 내부 로직이 끝나는 시점에,

이벤트를 해제시켜줘야한다. 왜냐하면 A요소의 버튼 클릭이벤트를 여러번 줄 경우.

A요소 클릭 시, 중복된 이벤트가 발생될수도 있기 때문이다.

 

useEffect(() => {
  function handleScroll(e) {
    console.log(window.scrollX, window.scrollY);
  }
  window.addEventListener('scroll', handleScroll);
  return () => window.removeEventListener('scroll', handleScroll);
}, []);

제품구매 예시로 보는 useEffect의 오류 방지

 

/api/buy 구매 API 를 호출하는 로직을 useEffect() 내부에 작성하였을 경우, 구매 완료 후, 뒤로 가기 했을때 다시 구매가 API가 호출되는 오류가 발생될수도 있기 때문에 API 로직을 별도의 함수로 뺴서 사용하는것이 좋다.

 

 

useEffect 내부에 구매 API 작성

useEffect(() => {
  // 🔴 Wrong: This Effect fires twice in development, exposing a problem in the code.
  fetch('/api/buy', { method: 'POST' });
}, []);

 

별도의 이벤트로 API 호출하는 로직을 구성

  function handleClick() {
    // ✅ Buying is an event because it is caused by a particular interaction.
    fetch('/api/buy', { method: 'POST' });
  }

 

 

 

https://react.dev/learn/synchronizing-with-effects

 

Synchronizing with Effects – React

The library for web and native user interfaces

react.dev

 

'React' 카테고리의 다른 글

[React] useEffect의 생명주기  (0) 2023.09.28
[React] 불필요한 useEffect 사용예시 - 참고용  (0) 2023.09.21
[React] Hooks  (0) 2023.09.21
[React] useRef  (0) 2023.09.20
[React] Vite React Project Setting  (0) 2023.09.20