본문 바로가기

FrontEnd

Suspense with a Custom Hook

반응형

훅의 장점은

인터랙션, 사이드이펙트, 메모이제이션을 추상화 / 캡슐화 할 수 있다는 점

위 로직들을 별도로 분리하여 재사용 할수 있다는 것이다.

 

아래와 같이, 데이터와 상태만 남기고 업데이트 코드를 전부 훅 안에 캡슐화 할 수 있다.

(isPending은 애니메이션 지연 시간 의미) 

const [pokemonResource, isPending] = usePokemonResource(pokemonName)

구현은 아래와 같이 한다.

const SUSPENSE_CONFIG = {
  timeoutMs: 4000,
  busyDelayMs: 300,
  busyMinDurationMs: 700,
}

const PokemonResourceCacheContext = React.createContext({})

function usePokemonResourceCache() {
  const cache = React.useContext(PokemonResourceCacheContext)
  return React.useCallback(
    name => {
      const lowerName = name.toLowerCase()
      let resource = cache[lowerName]
      if (!resource) {
        resource = createPokemonResource(lowerName)
        cache[lowerName] = resource
      }
      return resource
    },
    [cache],
  )
}

function createPokemonResource(pokemonName) {
  const data = createResource(fetchPokemon(pokemonName))
  const image = createResource(preloadImage(getImageUrlForPokemon(pokemonName)))
  return {data, image}
}

function usePokemonResource(pokemonName) {
  const [pokemonResource, setPokemonResource] = React.useState(null)
  const [startTransition, isPending] = React.useTransition(SUSPENSE_CONFIG)
  const getPokemonResource = usePokemonResourceCache()

  React.useEffect(() => {
    if (!pokemonName) {
      setPokemonResource(null)
      return
    }
    startTransition(() => {
      setPokemonResource(getPokemonResource(pokemonName))
    })
  }, [getPokemonResource, pokemonName, startTransition])

  return [pokemonResource, isPending]
}
반응형