지난 글에서는 Hooks가 무엇인가에 대해 알아보고 useState Hooks와 이를 이용한 useInput, useTabs라는 새로운 훅도 만들어보았다. 이번 글에서는 useEffect에 대해 알아보고 사용법과 이를 이용하여 새로운 훅들을 만들어 볼 것이다.
useEffect란?
useEffect의 역할은 3가지가 있다.
componentDidMount
componentWillUpdate
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이라는 함수를 호출하여 다시 렌더링되도록 하였다.
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에서는 아래와 같은 메시지 창을 띄울 수 있다.