useEffect과 useCallback

김동하·2023년 3월 9일
0

react

목록 보기
20/31

데이터 패칭과 useCallback, useEffect

위와 같은 앱이 있다.

  1. 포켓몬 이름의 버튼을 누르면 버튼 value에 해당하는 포켓몬 정보가 렌더 된다.

  2. input의 입력이 없거나 초기 상태면 특정한 text를 리턴한다.

  3. input에 포켓몬 이름을 입력 후 submit 버튼을 누르면 포켓몬 정보가 렌더 된다.

  4. 없는 이름을 누르면 에러 UI가 나온다.

  5. 데이터 패칭 중 loading UI를 띄운다.

이제 위의 요구 사항을 만족하는 모듈을 만들어보자.

빠르게 살펴보면 PokemonInfo 컴포넌트는 부모로부터 pokemonName을 받는다.

useAsync() 함수는 api 호출함수, status, dependency를 인자로 받아 내부 로직을 실행하여 state를 리턴한다.

useReducer() 부분은 비동기 패칭 시 status 처리 관련이라 생략해도 되고 useEffect() 안의 내용을 보면 먼저 asyncCallback()를 호출하고 promise 체인을 타면서 status를 업데이트 한다.

그리고 useAsync 함수는 state를 리턴한다.

문제점1

일단, useAsyncuseEffect()의 디펜더시를 인자로 받고 있다(lint에 걸림)

이렇게 디펜더시에 [asyncCallback] 넣어준다.

이제 이렇게 되면 문제가 생기는데 매렌더 마다 asyncCallback() 을 재생성하고 useAsync()는 디펜더시인 pokemonName 가 변경되지 않아도 asyncCallback()를 호출이 되면서 무한 렌더가 된다. 그래서 useCallback()으로 디펜더시가 변경될 때마다 렌더되도록 해야한다.

이렇게 useCallback() 사용해주면 무의미한 함수 생선을 막을 수 있다.

문제점2

다음 문제점이 발생한다. 리액트 공문에 보면 useMemo()에 이렇게 써있다.

그러니까, semantic guarantee란 말이 나오는데 뭔지 잘 모르겠고... useMemo, useCallback 같은 메모 함수를 사용할 때는 메모 함수 없이도 잘 동작하는 곳에 사용해라 라는 뜻인 것 같다.

위의 작성한 asyncCallback()useCallback() 없이는 제대로 작동하지 않는다. useCallback()은 내부 함수가 항상 안정적(stable)이 것이라 보증해 주지 않는다. 그러므로 asyncCallback()는 잘못 작동할 수도 있다.

개선

의존적이지 않은 안정적인 fetch 함수를 만들어보자

먼저, useAsync() 함수에 run() 함수를 추가한다. run() 함수는 useCallback()을 통해 호출 시만 실행된다. useCallback()의 의존성 배열이 비어도 되는 이유는 함수 내부에서 어느 것도 의존적인 것이 없기 떄문이다(= 안정적이다.).

PokemonInfo 컴포넌트에서 run() 을 통해 fetch를 하도록한다. 그리고 useEffect()에 디펜던시에 run함수를 추가한다. 결국, run()useAsync()에서 왔기 때문에 run()이 필요할 때만 변경된다고 보증할 수 있게 된다.

** 수정 : return 이 아님

가독성도 좋아진 것 같다????

결론

무지성으로 useEffect 썼는데 이제 생각하고 잘 써야겠다..

참고 -
https://kentcdodds.com/blog/usememo-and-usecallback
https://dongurami0502.tistory.com/32

profile
프론트엔드 개발

0개의 댓글