starkoora / wanted-pre-onboarding-challenge-fe-1

64 stars 1 forks source link

[질문] react-query를 이용한 suspense 처리 관련 질문 #33

Closed jinyoung234 closed 1 year ago

jinyoung234 commented 1 year ago
// useGetTodos.ts
import {getTodos} from '@/apis/main'
import {QUERY_KEY} from '@/constants'
import {ToDoInterface, ToDoListInterface} from '@/types'
import {useQuery} from '@tanstack/react-query'
import {AxiosError} from 'axios'

function useGetTodos() {
  const fallback: ToDoListInterface = {data: [] as ToDoInterface[]}
  const {
    data = fallback,
    isLoading,
    isError,
  } = useQuery([QUERY_KEY.TODO], getTodos)
  const todos = data?.data
  return {todos, isLoading, isError}
}

export default useGetTodos
// ToDoCardList.tsx
import {useGetTodos} from '@/queries'
import React from 'react'
import ToDoCard from '../ToDoCard'

const ToDoCardList = () => {
  const {todos, isLoading} = useGetTodos()
  if (isLoading) return <div>loading...</div>
  return (
    <section className='w-full flex flex-col items-center justify-center'>
      {todos.map((todo, _) => (
        <ToDoCard todo={todo} key={_} />
      ))}
    </section>
  )
}

export default ToDoCardList

로딩 state를 ToDoCardList에서 처리하는 것이 아닌 외부에서 처리하도록 변경하기 위해 suspense를 고려하였습니다..!

// _app.tsx
const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        suspense: true,
      },
    },
  })

우선 전역 queryClient의 defaultOptions에서 suspense를 true로 변경하였습니다.

// useGetTodos.ts
const {
    data = fallback,
    isLoading,
    isError,
  } = useQuery([QUERY_KEY.TODO], getTodos, {
    suspense: true,
  })

todos를 가져오는 useQuery custom hook에서도 susense 옵션을 true로 설정하였습니다.

// ToDoList.tsx
import {ToDoCreateForm} from '@/components/molecules'
import dynamic from 'next/dynamic'

const ToDoCardList = dynamic(() => import('@/components/molecules/ToDoCardList'), {
  ssr: false,
})

const ToDoList = () => {
  return (
    <div className='w-full pb-6'>
      <ToDoCreateForm />
      <ToDoCardList />
    </div>
  )
}

export default ToDoList

투두 생성 폼과 생성된 todo들의 card를 관리하는 컴포넌트인 ToDoList에서 dynamic import를 추가했으며, ssr 옵션을 false로 지정했습니다.

// ToDoCard.tsx
import {useGetTodos} from '@/queries'
import dynamic from 'next/dynamic'
import React from 'react'

const ToDoCard = dynamic(() => import('@/components/molecules/ToDoCard'), {
  ssr: false,
})

const ToDoCardList = () => {
  const {todos} = useGetTodos()
  return (
    <section className='w-full flex flex-col items-center justify-center'>
      <React.Suspense fallback={<div className='bg-black'>loading...</div>}>
        {todos.map((todo, _) => (
          <ToDoCard todo={todo} key={_} />
        ))}
      </React.Suspense>
    </section>
  )
}

export default ToDoCardList

custom hook에서 데이터를 받아와 컴포넌트가 그려지는 카드 컴포넌트에서도 마찬가지로 ssr 옵션 설정 후 dynamic import를 진행했으며 Suspense로 감싸 카드가 로딩 중이면 fallback UI가 보이도록 코드를 변경했습니다.

문제점

하지만 이렇게 코드를 구성했음에도 loading state가 그려지지 않는 형태로 컴포넌트가 그려져 고민 끝에 이슈를 남기게 되었습니다..! 작업 환경은 next v13.1.1, react v18.2.0, react-query v4.22.0 node v16.18.1 입니다..!

starkoora commented 1 year ago

제가 try catch에 비유드린 바 있는데요. 그 부분을 생각해보셔야 할거 같고요. 이 코드 상으로는 useGetTodos 보다 상위 스코프에 Suspense를 잡으셔야 합니다. @jinyoung234

jinyoung234 commented 1 year ago
import {Spinner} from '@/components/atoms'
import {ToDoCreateForm} from '@/components/molecules'
import dynamic from 'next/dynamic'
import React from 'react'

const ToDoCardList = dynamic(() => import('@/components/molecules/ToDoCardList'), {
  ssr: false,
  loading: () => <Spinner />,
})

const ToDoList = () => {
  return (
    <div className='w-full pb-6'>
      <ToDoCreateForm />
      <ToDoCardList />
    </div>
  )
}

export default ToDoList

멘토님의 조언대로 상위 스코프에서 처리하니까 해결되긴 했는데, suspense는 여전히 안먹혀서 dynamic import에서 제공하는 loading 메서드로 해결 했습니다.. ㅜㅜ suspense가 여전히 안 먹히는 이유는 더 공부를 해봐야 겠네여,,, 그 시간에 제가 회의 일정이 있어서 수업을 듣지 못했는데 try-catch에 대한 강사님이 정리해주신 react-query 문서로 제가 이해한건 loading state가 true가 아닌 false로 되어 suspense에서 처리 되기 전에 데이터를 가져와서 loading state가 안 보인걸로 이해할 수 있을까여..?!

starkoora commented 1 year ago

@jinyoung234 이 부분은 dynamic import를 사용하셨기 때문에 next js가 대신 처리해준걸로 이해하시면 될 것 같습니다 https://nextjs.org/docs/advanced-features/dynamic-import#example

The page will render the Suspense fallback first, followed by the Header component when the Suspense boundary is resolved.

jinyoung234 commented 1 year ago

감사합니다! ! 🙂