본문 바로가기

카테고리 없음

SuspenseList 통해 서스펜스 그룹화

반응형

Suspense로 코드스플리팅 해서 각 View를 비동기로 가져올때, 도착 순서대로만 보여준다면

레이아웃도 깨지면서 사용자들 눈에도 이상하게 보일 것이다.

보통 사람들이 기대하는 순서는 위아래, 왼오른쪽 배치이기 때문이다.

갑자기 아래쪽의 오른쪽 요소부터 나타난다면?

따라서 비동기 렌더링에도 질서가 필요하다.

<React.SuspenseList revealOrder="forwards">
  <React.Suspense fallback="Loading...">
    <ProfilePicture id={1} />
  </React.Suspense>
  <React.Suspense fallback="Loading...">
    <ProfilePicture id={2} />
  </React.Suspense>
  <React.Suspense fallback="Loading...">
    <ProfilePicture id={3} />
  </React.Suspense>
</React.SuspenseList>

SuspenseList 컴포넌트는 아래와 같은 props를 갖고 있다:

  • revealOrder: suspending 컴포넌트들이 그려질 순서
    • {undefined}: (default) 데이터가 도착하는 순서대로 
    • "forwards": 앞에 있는 컴포넌트가 suspending이 끝나야 다음 것을 보여줌 (앞->뒤)
    • "backwards": 뒤에 있는 컴포넌트가 suspending이 끝나야 다음 것을 보여줌 (뒤->앞)
    • "together": 한꺼번에 보여준다.
  • tail: suspending 컴포넌트들의 fallback 처리
    • {undefined}: (default) 전부 다 보여줌
    • "collapsed":  순서는 revealOrder 설정대로. 하나만 보여줌.
    • "hidden": 아무것도 안보여줌
  • children: other react elements which render <React.Suspense /> components. Note: <React.Suspense /> components do not have to be direct children as in the example above. You can wrap them in <div />s or other components if you need.

 

주의 : SuspensList의 revealOrder효과는 직계 자식에게만 전달된다.

주의 : tail은 모든 후손들에게 적용된다. 해당 효과를 피하려면 SuspenseList를 하나 더 사용한다.

이 경우 각기 div 안의 Suspense들은 revealOrder 적용받지 않음. collapsed는 적용됨

예제 : 포켓몬 은행

function App() {
  const [startTransition] = React.useTransition(SUSPENSE_CONFIG)
  const [pokemonResource, setPokemonResource] = React.useState(null)

  function handleSubmit(pokemonName) {
    startTransition(() => {
      setPokemonResource(createResource(fetchUser(pokemonName)))
    })
  }

  if (!pokemonResource) {
    return (
      <div className="pokemon-info-app">
        <div
          className={`${cn.root} totally-centered`}
          style={{height: '100vh'}}
        >
          <PokemonForm onSubmit={handleSubmit} />
        </div>
      </div>
    )
  }

  function handleReset() {
    setPokemonResource(null)
  }

  return (
    <div className="pokemon-info-app">
      <div className={cn.root}>
        <PokemonErrorBoundary
          onReset={handleReset}
          resetKeys={[pokemonResource]}
        >
          <React.SuspenseList revealOrder="forwards" tail="collapsed">
            <React.Suspense fallback={fallback}>
              <NavBar pokemonResource={pokemonResource} />
            </React.Suspense>
            <div className={cn.mainContentArea}>
              <React.SuspenseList revealOrder="forwards">
                <React.Suspense fallback={fallback}>
                  <LeftNav />
                </React.Suspense>
                <React.SuspenseList revealOrder="together">
                  <React.Suspense fallback={fallback}>
                    <MainContent pokemonResource={pokemonResource} />
                  </React.Suspense>
                  <React.Suspense fallback={fallback}>
                    <RightNav pokemonResource={pokemonResource} />
                  </React.Suspense>
                </React.SuspenseList>
              </React.SuspenseList>
            </div>
          </React.SuspenseList>
        </PokemonErrorBoundary>
      </div>
    </div>
  )
}

맨 위 헤더와 로그인 부분은 별개 룰이 적용된다. (상위 SuspenseList가 다름.) 일부러 tail 적용을 제거한 상태이다.

 

tail 적용 시, tail="collapsed"이기 때문에 하나만 나온다. tail은 자식 전체에 적용된다.
상위 컴포넌트 부분의 로딩이 끝난 상태이고, 하위 suspenseList의 tail을 제외한 상태이면 이렇게 된다.
suspenseList에 tail을 적용했다.
왼쪽 네비게션을 먼저, 그 다음 오른쪽 컨텐츠들을 보여준다 (forwards - html 순서대로)

+Left와 RightNev는 동시에 보여준다.

참고

Concurrent Mode API Reference (Experimental) – React (reactjs.org)

 

Concurrent Mode API Reference (Experimental) – React

A JavaScript library for building user interfaces

reactjs.org

다시보니 이 게시물은 나중에 추가적으로 설명을 더 달아야곘다.

반응형