useEffect 완벽가이드 기억하고 싶은 내용 스크랩

useEffect 완벽가이드

  • 함수형 컴포넌트: 렌더링 당시의 상태값 조회
  • 클래스 컴포넌트: this.state는 언제나 최신값 조회 (이를 해결하기 위해 클로저 사용).
  • 리액트는 함수를 호출해보지 않고 함수가 어떤 일을 하는지 알아낼 수 없습니다.
  • 이펙트의 실행시점: 브라우저가 화면에 DOM을 그린 이후
  • 흔한 실수 중 하나가 함수는 의존성에 포함되면 안된다는 것입니다.
  • 어떠한 함수를 이펙트 안에서만 쓴다면, 그 함수를 직접 이펙트 안으로 옮기세요.
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
  // ...
}
  • 이러면 뭐가 좋아지냐구요? 우리는 더 이상 “옮겨지는 의존성” 에 신경 쓸 필요가 없습니다. 의존성 배열은 더 이상 거짓말 하지 않습니다. 진짜로 이펙트 안에서 컴포넌트의 범위 바깥에 있는 그 어떠한 것도 사용하지 않고 있습니다.

하지만 저는 이 함수를 이펙트 안에 넣을 수 없어요

  1. 함수가 컴포넌트 스코프 안의 어떠한 것도 사용하지 않는다면, 컴포넌트 외부로 끌어올려두고 이펙트 안에서 자유롭게 사용하면 됩니다.
// ✅ 데이터 흐름에 영향을 받지 않는다
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
  // ...
}
  1. 혹은 useCallback 훅으로 감쌀 수 있습니다.
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
  // ...
}
  • useCallback 덕분에 query 가 같다면, getFetchUrl 또한 같을 것이며, 이펙트는 다시 실행되지 않을 것입니다. 하지만 만약 query 가 바뀐다면, getFetchUrl 또한 바뀌며, 데이터를 다시 페칭할 것입니다. 마치 엑셀 스프레드시트에서 어떤 셀을 바꾸면 다른 셀이 자동으로 다시 계산되는 것과 비슷합니다.
  1. 부모로부터 함수 prop을 내려보내는 것 또한 같은 해결책이 적용됩니다.
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 은 꽤 좋은 돌파구이며 함수가 전달되어 자손 컴포넌트의 이펙트 안에서 호출되는 경우 유용합니다. 아니면 자손 컴포넌트의 메모이제이션이 깨지지 않도록 방지할 때도 쓰입니다. 하지만 훅 자체가 콜백을 내려보내는 것을 피하는 더 좋은 방법을 함께 제공합니다.

좋은 웹페이지 즐겨찾기