본문 바로가기

Web Develop/React

React Hooks에 대해 알아보자. (2)

지난 글에서는 Hooks가 무엇인가에 대해 알아보고 useState Hooks와 이를 이용한 useInput, useTabs라는 새로운 훅도 만들어보았다. 이번 글에서는 useEffect에 대해 알아보고 사용법과 이를 이용하여 새로운 훅들을 만들어 볼 것이다.

 

useEffect란?

useEffect의 역할은 3가지가 있다.

  1. componentDidMount
  2. componentWillUpdate
  3. componentWillMount

즉, 리액트의 생명 주기를 관리할 수 있다는 것이다. 또한 위의 3가지 상황에 모두 동작하는 것이 아니라, 선택해서 특정 상황에만 렌더링되도록 할 수도 있다.

먼저 useEffect를 어떻게 사용하는지 살펴보자.

 

useEffect(함수, [deps]);

 

useEffect는 첫 번째 인자로 마운트되었을 때 실행할 함수를 전달하고, 두 번째 인자로는 dependency로 component가 이 배열 안에 있는 값일 때만 값이 변하도록 활성화된다.

따라서 useEffect의 실행 과정은 다음과 같다.

componentDidMount는 새로고침했을 때 해당 함수가 한 번 실행되게 된다. componentWillUpdate는 특정 값이 변할 때 다시 렌더링되어 useEffect의 2번째 인자 dependency에 따라 다르게 동작한다. dependency가 아예 없으면 어떤 것이 변경될 때마다 계속 렌더링되게 되고, 빈 배열로 주게 되면 다시 렌더링되지 않으며, 배열 안에 어떤 값을 주면 그 값이 변경될 때마다 렌더링된다. 그리고 componentWillUnMount는 useEffect 안에 return 값으로 주면 실행되는데 그 예시는 아래의 useClick Hook에서 볼 수 있다.

 

import React, { useState, useEffect } from "react";
import "./styles.css";

export default function App() {
  const sayHello = () => console.log("Hello");

  const [number, setNumber] = useState(0);
  const [anumber, setAnumber] = useState(0);

  useEffect(sayHello, [number]); 
  // mount될 때와 number일 때만 실행 -> componentDidMount, componentWillUpdate
  // useEffect로부터 function이 리턴 -> componentWillUnmout

  // componentWillUnmount, componentDidMount, componentWillUpdate 역할
  // componentDidMount: 새로고침했을 때 실행
  // componentWillUpdate: 값이 업데이트되었을 때 실행

  // useEffect는 2개의 인자를 받음
  // - 첫 번째 인자: function으로써의 effect
  // - 두 번째 인자: dependency [] (리스트에 있는 값일 때만 값이 변하도록 활성화됨)
  // component가 Mount되었을 때만 실행시키고 그 뒤에 어떤 경우에도 실행시키고 싶지 않다면 [] 빈 dependency를 전달해주면 됨 

  return (
    <div className="App">
      <h1>Hello</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button onClick={() => setNumber(number + 1)}>{number}</button>
      <button onClick={() => setAnumber(anumber + 1)}>{anumber}</button>
    </div>
  );
}

 

위의 예시는 number가 변경될 때마다 렌더링되도록 하였다.

 

1. useTitle (useState, useEffect 사용)

첫 렌더링이 된 후에 탭의 글자(제목)를 변경할 때 사용한다.

setTimeout을 통해 5초 동안 Loading…이라는 문구가 뜨고 그 후에 Home으로 문구가 변경되도록 구현한 코드이다. useEffect를 사용하여 title이 변경될 때만 updateTitle이라는 함수를 호출하여 다시 렌더링되도록 하였다.

 

import React, { useState, useEffect } from "react";
import "./styles.css";

function useTitle(initialTitle) {
  const [title, setTitle] = useState(initialTitle);
  const updateTitle = () => {
    const htmlTitle = document.querySelector("title");
    htmlTitle.innerText = title;
  };
  useEffect(updateTitle, [title]);
  return setTitle;
}

export default function App() {
  const titleUpdater = useTitle("Loading...");
  setTimeout(() => titleUpdater("Home"), 5000);
  return (
    <div className="App">
      <h1>Hello</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

 

2. useClick (useEffect, useRef 사용)

사용자가 어떤 요소를 클릭했을 때 어떤 것이 실행되도록 하고 싶을 때 사용한다.

  • useEffect에서 dependency를 빈 배열로 함으로써 컴포넌트가 마운트될 때만 1번만 useEffect 안에 있는 것들이 실행되도록 하였다. 또한 useEffect안에 리턴 문을 작성하였는데 이는 componentWillunMount일 때만 동작하는 예시로, component가 마운트되지 않았을 때 eventListener가 배치되지 않도록 하기 위함이다.

 

import React, { useState, useEffect, useRef } from "react";
import "./styles.css";

const useClick = (onClick) => {
  if (typeof onClick !== "function") {
    return;
  }
  const element = useRef();
  useEffect(() => { // componentDidMount 상태에 동작
    if (element.current) {
      element.current.addEventListener('click', onClick);
    } // 이벤트가 발생한 뒤 정리해줄 필요가 있음 -> componentWillUnMount일 때 동작
    // component가 mount되지 않았을 때 eventListener가 배치되지 않도록 하기 위함
    return () => {
      if (element.current) {
        element.current.removeEventListener('click', onClick);
      }
    }
  }, []);
  return element;
}

export default function App() {
  const sayHello = () => console.log('say hello');
  // react에 있는 모든 component는 reference element를 가지고 있음
  const title = useClick(sayHello);  

  return (
    <div className="App">
      <h1 ref={title}>Hello</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

 

3. useHover

useClick가 동일하다. 단순히 이벤트를 click에서 mouseover(hover)로 바꾼 것이다.

 

import React, { useState, useEffect, useRef } from "react";
import "./styles.css";

const useHover = (onHover) => {
  if (typeof onHover !== "function") {
    return;
  }
  const element = useRef();
  useEffect(() => { // componentDidMount 상태에 동작
    if (element.current) {
      element.current.addEventListener('mouseover', onHover);
    } // 이벤트가 발생한 뒤 정리해줄 필요가 있음 -> componentWillUnMount일 때 동작
    // component가 mount되지 않았을 때 eventListener가 배치되지 않도록 하기 위함
    return () => {
      if (element.current) {
        element.current.removeEventListener('mouseover', onHover);
      }
    }
  }, []);
  return element;
}

export default function App() {
  const sayHello = () => console.log('say hello');
  // react에 있는 모든 component는 reference element를 가지고 있음
  const title = useHover(sayHello);  

  return (
    <div className="App">
      <h1 ref={title}>Hello</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

 

4. UseConfirm

만약 사용자가 어떤 요소를 삭제하려고 할 때 확인 메시지를 띄우도록 하고 싶을 때 사용한다.

확인 메시지에서 확인 버튼을 누르게 되면 매개변수 onConfirm로 받아온 함수를 실행하고 취소 버튼을 누르게 되면 onCancel에 해당하는 함수를 실행한다.

 

import React, { useState, useEffect, useRef } from "react";
import "./styles.css";

const useConfirm = (message = "", onConfirm, onCancel) => {
  if (onConfirm && typeof onConfirm !== "function") {
    return;
  }

  if (onCancel && typeof onCancel !== "function") {
    return;
  }

  const confirmAction = () => {
    if (window.confirm(message)) {
      onConfirm();
    } else {
      onCancel();
    }
  };
  return confirmAction;
};

export default function App() {
  const deleteWorld = () => console.log("Deleting the world...");
  const abort = () => console.log("Aborted");
  const confirmDelete = useConfirm("Are you sure?", deleteWorld, abort);

  return (
    <div className="App">
      <h1>Hello</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button onClick={confirmDelete}>Delete the world</button>
    </div>
  );
}

 

5. usePreventLeave

사용자가 페이지를 나갈 때(페이지의 x 버튼을 누를 때) 확인 메시지를 띄우고 싶을 때 사용한다.

beforeunload 이벤트가 발생할 때 메시지를 띄우는 것으로, useEffect와 같은 다른 훅을 사용하지 않고 이 이벤트 사용법에 따라 작성하면 chrome에서는 아래와 같은 메시지 창을 띄울 수 있다.

 

 

import React, { useState, useEffect, useRef } from "react";
import "./styles.css";

const usePreventLeave = () => {
  const listener = (event) => {
    event.preventDefault();
    event.returnValue = "";
  };
  const enablePrevent = () => window.addEventListener("beforeunload", listener);
  const disablePrevent = () =>
    window.removeEventListener("beforeunload", listener);

  return { enablePrevent, disablePrevent };
};

export default function App() {
  const { enablePrevent, disablePrevent } = usePreventLeave();

  return (
    <div className="App">
      <h1>Hello</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button onClick={enablePrevent}>Protect</button>
      <button onClick={disablePrevent}>UnProtect</button>
    </div>
  );
}

 

 

Window: beforeunload 이벤트 - Web API | MDN

beforeunload 이벤트는 window, 문서(document) 및 해당 리소스가 언로드(unload) 되려고 할 때 시작됩니다. 문서는 계속 보이고 있으며 이벤트는 이 시점에서도 취소할 수 있습니다.

developer.mozilla.org

 

💡 event.preventDefault();와 event.returnValue = ""; 둘다 적어야 하는 이유?
event.preventDefault();
event.returnValue = "";​

대부분의 브라우저에서 호환되도록 하기 위해서이다.

 

 

'Web Develop > React' 카테고리의 다른 글

React Hook에 대해 알아보자.(1)  (0) 2023.07.13
[React] 참고하면 좋은 사이트  (0) 2022.05.10