코딩/React
useEffect를 이용한 커스텀훅을 만들어보자(feat. useAsyncEffect)
2워노
2024. 2. 29. 23:53
1. useAsyncEffect
- useAsyncEffect는 React Hooks중 하나인 useEffect를 통해 제작한 커스텀 훅입니다.
- 기본적으로 useEffect를 비동기 작업에 사용 할 수 있게 확장한 커스텀 훅이라고 할 수 있습니다.
- 이를 사용하면, useEffect 내에서 async/await를 사용하여 비동기 작업을 수행할 수 있습니다
- (useAsyncEffect 코드)
import { useEffect, DependencyList } from "react"
export const useAsyncEffect = (asyncEffect: () => Promise<void>, deps?: DependencyList) => {
useEffect(() => {
const asyncEffectWrapper = async () => {
await asyncEffect()
}
void asyncEffectWrapper()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps)
}
2. 코드 설명
- useAsyncEffect는 두 개의 인자를 받습니다. 첫 번째 인자는 Promise를 반환하는 async 함수이고, 두 번째 인자는 DependencyList입니다. 두 번째 인자는 선택적으로 사용할 수 있으며, deps가 변경될 때마다 useEffect가 호출되도록 합니다.
- useEffect 내부에서 asyncEffectWrapper라는 비동기 함수를 선언하고, 호출합니다. 이 함수는 asyncEffect 함수를 비동기로 실행합니다.
- 'void asyncEffectWrapper()'는 'asyncEffectWrapper()'가 Promise를 반환하기 때문에 Promise를 처리하지 않는 경고를 방지하기 위해 사용됩니다.
- 마지막으로, 'eslint-disable-next-line react-hooks/exhaustive-deps' 주석은 ESLint 경고를 무시하도록 하는 주석입니다. 이는 deps 배열 내부의 값들이 useEffect 내부에서 사용되지 않을 때 발생하는 경고를 방지합니다.
- 이로써, useAsyncEffect를 사용하면 useEffect를 이용하여 비동기 작업을 더욱 효과적으로 처리할 수 있습니다.
3. 그냥 useEffect로 비동기 작업을 하면 되는거 아닌가?
- React의 useEffect Hook은 순수 함수여야 합니다. 즉, useEffect 함수는 동기적으로 실행되며, 이외의 효과를 발생시키지 않아야 합니다.
- 그러나 비동기 함수는 Promise를 반환하게 됩니다. 따라서 useEffect 내부에서 비동기 함수를 직접 사용하게 되면, useEffect가 Promise를 반환하게 되어 순수 함수가 아니게 됩니다.
useEffect(async () => {
// ... 비동기 작업
}, []);
- 위와 같이 작성하면, useEffect는 비동기 함수를 호출한 결과인 Promise를 반환하게 됩니다. 이는 useEffect의 설계 원칙에 위배됩니다.
4. 그렇다면 왜 useEffect가 순수함수여야만 할까?
- 이는 useEffect의 클린업 메커니즘과 관련이 있습니다. useEffect는 선택적으로 클린업 함수를 반환할 수 있습니다. 이 클린업 함수는 useEffect가 재실행되기 전이나 컴포넌트가 언마운트될 때 호출됩니다.
useEffect(() => {
// ... 효과를 발생시키는 코드
return () => {
// ... 클린업 코드
};
}, []);
- 위와 같이 useEffect가 클린업 함수를 반환하면, React는 이 함수를 기억하고 적절한 시점에 호출합니다. 그러나 useEffect가 Promise를 반환하게 되면, React는 이를 클린업 함수로 해석할 수 없으며, 이로 인해 예상치 못한 동작이 발생할 수 있습니다.
- 따라서 useEffect 내부에서 비동기 작업을 처리하려면, 별도의 async 함수를 선언하고 이를 호출하는 방식을 사용해야 합니다. 이렇게 하면 useEffect는 여전히 순수 함수를 유지하면서 비동기 작업을 처리할 수 있습니다. (아래 예시 참조)
5. (예시) useEffect vs useAsyncEffect
✨ useEffect
import { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
setData(data);
};
fetchData();
}, []);
if (!data) {
return <div>Loading...</div>;
}
return <div>{data}</div>;
}
- useEffect에서는 비동기 함수를 직접 사용할 수 없으므로, 내부에 비동기 함수를 선언하고 호출하는 방식을 사용합니다.
✨useAsyncEffect
import { useState } from 'react';
import { useAsyncEffect } from './useAsyncEffect';
function MyComponent() {
const [data, setData] = useState(null);
useAsyncEffect(async () => {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
setData(data);
}, []);
if (!data) {
return <div>Loading...</div>;
}
return <div>{data}</div>;
}
- useAsyncEffect를 사용하면 useEffect 내부에서 별도로 비동기 함수를 선언하고 호출하는 과정 없이 바로 async 함수를 사용할 수 있습니다. 이를 통해 코드의 가독성이 향상되고, 비동기 작업을 더욱 직관적으로 처리할 수 있습니다.
6. 결론
- useAsyncEffect는 useEffect를 비동기 작업에 적합하게 확장한 커스텀훅으로, 비동기 작업을 더욱 효율적이고 간결하게 처리할 수 있습니다!