맨 땅에 프론트엔드 개발자 되기

React useEffect와 addEventListener - window 이벤트 렌더링 규칙 본문

코딩 공부 일지/React JS

React useEffect와 addEventListener - window 이벤트 렌더링 규칙

헬로코딩 2022. 4. 4. 18:21
728x90

useEffect 란?

React 의 함수형 컴포넌트에서 라이프사이클을 감지하기 위한 HOOK 이다. 처음에 라이프사이클이란 말을 들었을 때, 너무 거창한 단어가 붙어있는 것 같아서 이해가 잘 되지 않았었다. 그러나 React가 CSR(클라이언트 사이드 렌더링) 방식이라는 점을 기억하고 있으면, 렌더링 규칙에 대해 알고 있어야 컴포넌트 제어가 가능하다는 것을 느낄 수 있다.

- React 라이프사이클

대표적인 라이프사이클에는 아래와 같은 것들이 있다.

라이프사이클 명 설명
componentDidMount 컴포넌트의 첫번째 렌더링을 마치고 난 뒤 호출되는 메소드다. 
이미 컴포넌트가 화면에 나타난 상태이고, 이 시점에 보통 해당 컴포넌트에서 필요로 하는 데이터를 요청하기 위해 axios, fetch 등을 이용해 데이터를 불러온다.
shouldComponentUpdate 컴포넌트가 업데이트 되어야 할지 말지 결정하는 시점이다. 주로 최적화와 관련된 메소드다.
componentDidUpdate 리렌더링이 일어난 후에 호출되는 메소드다. 
componentWillUnmount 컴포넌트가 언마운트 되기 직전에 호출되는 메소드다. 여기서는 주로 DOM에 직접 등록했었던 이벤트를 제거하고, 만약에 setTimeout 을 사용했다면, clearTimeout으로 제거를 해준다.

- useEffect 가 라이프사이클을 구분하는 방법

함수형 컴포넌트에서는 각각의 라이프사이클 메소드를 useEffect 하나로 통일시킬 수 있다. 대신 dependency를 선언해줘 시점을 구분한다.

useEffect(() => {
    console.log('hi')
  }, [/* dependency 선언하는 곳 */])

useEffect 안에 콜백함수를 불러온 뒤 2번째 인자의 배열 안에 업데이트의 기준이 되는 상태를 선언하면 그 상태의 값에 따라 컴포넌트를 업데이트 할 수 있고, 빈 배열을 선언하면 최초 로드 시에만 렌더링이 된다.

useEffect와 addEventListener

React에서 window나 document에 addEventListener를 통해 이벤트를 추가할 때, 그냥 사용하게 되면 렌더링 시점이 구분이 되지 않기 때문에 에러가 난다. useEffect를 이용해서 최초 로드 시에 이벤트를 추가해주면 이벤트 감지가 시작되어 에러를 일으키지 않는다.

 

나는 특정 DOM이 마우스의 움직임에 따라 따라다니도록 구현하고 싶었는데, 그냥 이벤트를 등록하지 자꾸 에러가 났다. 구글링을 통해 useEffect를 사용해야 한다는 것을 알게 되었다.

import { useEffect, useRef, useState } from 'react';

export default function TextBtn() {
  const textBox = useRef();
  const [circlePosition, setCirclePosition] = useState({
    left: 0,
    top: 0
  })

  useEffect(() => {
    document.addEventListener('mousemove', (e) => {
       setCirclePosition({left: e.clientX - 500, top: e.clientY - 500})
    })
  }, [])

  return (
    <>
    <section className={styles.textWrap}>
      <h2>
        <div 
        ref={textBox}
        className={styles.textBg}>
          <div 
          className={`${styles.textBox} ${styles.textBox1}`}
          style={{left: circlePosition.left, top: circlePosition.top}}></div>
          <div 
          className={`${styles.textBox} ${styles.textBox2}`}
          style={{left: circlePosition.left, top: circlePosition.top}}></div>
          <div 
          className={`${styles.textBox} ${styles.textBox3}`}
          style={{left: circlePosition.left, top: circlePosition.top}}></div>
        </div>
      </h2>
    </section>
    </>
  )
}

위와 같이 dependency를 최초 로드 시로 만들고 이벤트를 등록해주니 이벤트가 잘 작동되었다.

 

이벤트 등록 해제하기

addEventListner를 통해 이벤트를 등록하고 난 뒤 이벤트 등록을 해제해주지 않으면, mouseover 혹은 scroll 이벤트의 경우 계속해서 이벤트를 감지하기 때문에 성능 저하를 일으킬 수 있다. 그러므로 컴포넌트가 언마운트 될 때 꼭 이벤트 등록을 해제해주어야 한다.

 

이벤트 등록을 해제할 때는 useEffect의 내부 함수의 return 값으로 removeEventListener를 통해 해제해주면 된다.

import { useEffect, useRef, useState } from 'react';

export default function TextBtn() {
  const textBox = useRef();
  const [circlePosition, setCirclePosition] = useState({
    left: 0,
    top: 0
  })

  useEffect(() => {
    const onMouseMove = (e) => {
      setCirclePosition({left: e.clientX - 500, top: e.clientY - 500})
    };

    document.addEventListener('mousemove', onMouseMove);

    return () => {
      document.removeEventListener('mousemove', onMouseMove);
    }
  }, [])

  return (
    <>
    <section className={styles.textWrap}>
      <h2>
        <div 
        ref={textBox}
        className={styles.textBg}>
          <div 
          className={`${styles.textBox} ${styles.textBox1}`}
          style={{left: circlePosition.left, top: circlePosition.top}}></div>
          <div 
          className={`${styles.textBox} ${styles.textBox2}`}
          style={{left: circlePosition.left, top: circlePosition.top}}></div>
          <div 
          className={`${styles.textBox} ${styles.textBox3}`}
          style={{left: circlePosition.left, top: circlePosition.top}}></div>
        </div>
      </h2>
    </section>
    </>
  )
}

 

  1. useEffect의 콜백함수를 통해 document 객체에 addEventListner가 이벤트를 바인딩한다. 
  2. useEffect는 최초 렌더링이 끝나고 나서 실행된다.
  3. useEffect 내부에 addEventListner로 실행되는 이벤트를 감지하는 감시자가 있다.
  4. eventListner는 이벤트 트리거가 작동되기 만을 계속 기다리고 있을 수 있다. 이벤트를 종료시켜야 한다면, return 값으로 removeEventListner를 실행시켜주면 된다.
728x90