useEffect 완벽가이드
function SearchResults() {
const [query, setQuery] = useState('react');
useEffect(() => {
function getFetchUrl() {
return 'https://hn.algolia.com/api/v1/search?query=' + query;
}
async function fetchData() {
const result = await axios(getFetchUrl());
setData(result.data);
}
fetchData();
}, [query]); // ✅ Deps는 OK
// ...
}
// ✅ 데이터 흐름에 영향을 받지 않는다
function getFetchUrl(query) {
return 'https://hn.algolia.com/api/v1/search?query=' + query;
}
function SearchResults() {
useEffect(() => {
const url = getFetchUrl('react');
// ... 데이터를 불러와서 무언가를 한다 ...
}, []); // ✅ Deps는 OK
useEffect(() => {
const url = getFetchUrl('redux');
// ... 데이터를 불러와서 무언가를 한다 ...
}, []); // ✅ Deps는 OK
// ...
}
function SearchResults() {
// ✅ 여기 정의된 deps가 같다면 항등성을 유지한다
const getFetchUrl = useCallback((query) => {
return 'https://hn.algolia.com/api/v1/search?query=' + query;
}, [query]); // ✅ 콜백의 deps는 OK
useEffect(() => {
const url = getFetchUrl('react');
// ... 데이터를 불러와서 무언가를 한다 ...
}, [getFetchUrl]); // ✅ 이펙트의 deps는 OK
useEffect(() => {
const url = getFetchUrl('redux');
// ... 데이터를 불러와서 무언가를 한다 ...
}, [getFetchUrl]); // ✅ 이펙트의 deps는 OK
// ...
}
function Parent() {
const [query, setQuery] = useState('react');
// ✅ query가 바뀔 때까지 항등성을 유지한다
const fetchData = useCallback(() => {
const url = 'https://hn.algolia.com/api/v1/search?query=' + query;
// ... 데이터를 불러와서 리턴한다 ...
}, [query]); // ✅ 콜백 deps는 OK
return <Child fetchData={fetchData} />
}
function Child({ fetchData }) {
let [data, setData] = useState(null);
useEffect(() => {
fetchData().then(setData);
}, [fetchData]); // ✅ 이펙트 deps는 OK
// ...
}
그렇다고 useCallback 을 어디든지 사용하는 것은 꽤 투박한 방법이라고 강조하고 싶습니다. useCallback 은 꽤 좋은 돌파구이며 함수가 전달되어 자손 컴포넌트의 이펙트 안에서 호출되는 경우 유용합니다. 아니면 자손 컴포넌트의 메모이제이션이 깨지지 않도록 방지할 때도 쓰입니다. 하지만 훅 자체가 콜백을 내려보내는 것을 피하는 더 좋은 방법을 함께 제공합니다.