useEffect는 리액트 컴포넌트가 렌더링 될때마다 특정 작업을 수행하도록 설정할 수 있는 Hook이다. 클래스형 컴포넌트의 componentDidMount
와 componentDidUpdate
를 합친 형태로 보아도 무방하다.
useEffect로 전달된 함수는 레이아웃 배치, 화면 그리기가 완료될 때까지 지연된 뒤 실행된다. (화면 그리는걸 막으면 안되기 때문에) 그리고 다음 렌더링이 발생하기 전에 실행되는 것도 보장된다. 물론 대부분의 작업이 렌더링을 차단해서는 안되지만, 사용자가 UI데이터와 화면의 내용 불일치를 경헙하지 않도록 동기적으로 Effect를 발생시키고 싶다면 useLayoutEffect
함수를 이용하면 된다.
- 부수효과를 처리하기 위한 훅
- useEffect는 화면이 다 그려진 뒤 실행된다.
- useLayoutEffect는 화면이 그려지기 이전시점에 실행된다.
useEffect(() => {
console.log("side effect");
}, [variable]);
첫번째 매개변수로 함수를 입력한다. 이 함수는 컴포넌트가 렌더링 된 후에 호출된다. 렌더링 결과가 실제 돔에 반영되고 비동기로 호출된다. 이 함수를 부수 효과 함수라고 한다. 두번째 매개변수로 배열을 입력한다. 이 배열은 의존성 배열이라고 부른다.
만약 컴포넌트가 화면에 가장 처음 렌더링 될 때만 실행되고 업데이트할 경우 실행할 필요가 없을 때 함수의 두번째 파라미터로 비어있는 배열을 넣어주면 된다.
useEffect(()=> {
console.log('마운트 될 때만 실행됩니다.')
},[])
컴포넌트가 처음 나타날 때만 실행되고 그 이후에는 콘솔에 문구가 나타나지 않는다.
componentDidUpdate(prevProps, prevState) {
if (prevProps.value !== this.props.value) {
doSomething();
}
}
위 코드에서는 props 안에 들어있는 value값이 바뀔 때에만 특정 작업을 수행하도록 한다. 만약 이러한 작업을 useEffect
에서 해야한다면 두번째 파라미터로 전달되는 배열안에 검사하고 싶은 값을 넣어주면 된다.
useEffect(() => {
console.log(value);
}, [value]);
useEffect(()=> {
console.log('effect');
console.log(name);
return() => {
console.log('cleanup');
console.log(name);
};
}, []);
- cleanup 함수 ( return 뒤에 나오는 함수, useEffect에 대한 뒷정리 함수라고 한다.)
언마운트 될때만 clean함수를 실행하고 싶다면 두번째 파라미터에 빈 배열을 넣으면 된다. 특정 값이 업데이트되기 직전에 clean함수를 실행하고 싶을 때 두번째 파라미터 배열안에 검사하고 싶은 값을 넣어주면 된다.
import React, { useState, useEffect } from 'react'
function getUserAPI(userID) {
// .... 비동기통신
return new Promise(resolve=>{
resolve({
name : 'jack',
age : '30'
})
})
}
export default function Profile({userID}) {
const [user,setUser] = useState(null);
useEffect(()=>{
getUserAPI(userID).then(data=>setUser(data))
},[userID]);
return (
<div>
{!user && <p>사용자 정보를 가져오는중...</p>}
{user && (
<>
<p>{`name is ${user.name}`}</p>
<p>{`age is ${user.age}`}</p>
</>
)}
</div>
);
}
useEffect
함수는 두버내 매개변수로 의존성배열을 받습니다. useEffect
훅에서 API통신을 하면 렌더링할 때마다 호출되기 때문에 불필요하게 동작합니다. useEffect함수의 두번째 매개변수로 배열을 입력하면, 배열의 값이 변경되는 경우에만 함수가 호출됩니다. 여기에서는 userID
가 변경되는 경우에만 호출됩니다.
import React, {useState, useEffect} from "react";
export default function App() {
const [image, setImage] = useState({});
useEffect(() => {
async function fetchImage() {
const res = await fetch("https://dog.ceo/api/breeds/image/random");
const json = await res.json();
setImage(json);
}
fetchImage();
});
console.log("image", image);
return (
<div className="App">
<img src={(image || {}).message} />
</div>
);
}
useEffect
에 의존성 배열을 비워두었다는 것을 알 수 있습니다. 이것은 매우 좋지 않은 행동이며, 이렇게 할 경우 매 렌더링마다 이펙트 함수가 실행될 것입니다.
- 이팩트 함수 내에서 image state값이 변경됨
- 값이 변경됨을 인지하고 이펙트 함수가 실행됨
- 위 과정이 반복
그렇다면, 위의 useEffect
에서 의존성 배열에는 어떤 값이 포함되어야 할까요? 현재 이펙트 함수 내에서 사용되는 변수는 setImage
가 전부입니다. 따라서 [setImage] 값을 전달해야 합니다. 또한, setImage 값은 useState에서 반환하는 setter로, 매 렌더링시에 다시 생성되지 않아서 이펙트 함수가 한 번만 실행됩니다.