ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Nextjs]SSG,SSR react-query로 Dynamic Routes에 적용
    Next.js 2022. 12. 22. 16:07
    728x90

    _app.tsx

    import '../styles/globals.css';
    import React from 'react'; 
    import type { AppProps } from 'next/app';
    import { Hydrate, QueryClient, QueryClientProvider } from 'react-query';
    import { RecoilRoot } from 'recoil';
    
    function MyApp({ Component, pageProps }: AppProps) {
      const queryClient = React.useRef(new QueryClient());
    
      //const [queryClient] = React.useState(() => new QueryClient())
    
      return (
        <QueryClientProvider client={queryClient.current}>
        {/* <QueryClientProvider client={queryClient}> */}
          <Hydrate state={pageProps.dehydratedState}>
            <RecoilRoot>
              <Component {...pageProps} />
            </RecoilRoot>
          </Hydrate>
        </QueryClientProvider>
      );
    }
    
    export default MyApp

    [postId].tsx

    import type { GetServerSideProps, GetStaticPaths, GetStaticProps, InferGetStaticPropsType, NextPage } from 'next'
    import Image from 'next/image';
    import Format from '../../layout/format';
    import Author from '../../components/_child/author';
    import Ralated from "../../components/_child/ralated"
    import { ParsedUrlQuery } from 'querystring';
    import { dehydrate, QueryClient, useQuery } from 'react-query';
    import { useRouter } from 'next/router';
    import Error from '../../components/_child/error';
    import Spinner from '../../components/_child/spinner';
    
    interface PostsProps {
        id:Number;
        title:string;
        subtitle:string;
        category:string;
        img:string;
        description:string;
        published:string;
        author:{
            name:string;
            img:string;
            designation:string;
        }
    }
    
    const page = () => {
        
        const router = useRouter();
        const { postId } = router.query;
        //const { data, isLoading, isError } = useQuery(["post"], () => getPost(postId ? postId : 1));
        const { data, isLoading, isError } = useQuery<PostsProps>(["post"], () => getPost(postId ? postId : 1));
    
        //const {id, title, subtitle, description, category, img, published, author } = posts;
        
        if (isLoading) {
            return <Spinner></Spinner>;
        }
        if (isError) {
            return <Error></Error>;
        }
        
        //const {id, title, subtitle, description, category, img, published, author } = data;
        //const {id, title, subtitle, description, category, img, published, author }:PostsProps = data;
        return (
            <Format>
            <section className=" container mx-auto md:px-2 py-16 lg:w-1/2">
                <div className=" flex justify-center ">
                    { data?.author ? <Author author={data?.author}></Author> : null }
                </div>
                <div className="post py-10">
                    <h1 className=" font-bold text-4xl text-center pb-5">{data?.title || null}</h1>
                    <p className=" text-gray-500 text-xl text-center">{data?.subtitle || null}</p>
                    <div className="py-10">
                    <Image src={data?.img || "/"} width={900} height = {600}></Image>
                    </div>
                    <div className="content text-gray-600 text-lg flex flex-col gap-4">
                    {data?.description || null}
                    </div>
                </div>
                <Ralated></Ralated>
            </section>
            </Format>
        );
        
    }
    
    export default page;
    
    interface IParams extends ParsedUrlQuery {
        postId: string
    }
    
    const getPost = async (postId:string | string[] | number) => await (await fetch(`http://localhost:3000/api/posts/${postId}`)).json();
    
    // export const getServerSideProps: GetServerSideProps = async (context) => {
        
    //     const queryClient = new QueryClient();
    //     const { postId } = context.params as IParams;
    
    //     await queryClient.prefetchQuery<PostsProps>(["post", postId], () => getPost(postId ? postId : 1));
        
        
    //     return {
    //         props: {
    //             dehydratedState: dehydrate(queryClient),
    //         }
    //     }
    // }
    
    // export const getServerSideProps: GetServerSideProps = async (context) => {
        
    //     const baseURL = "http://localhost:3000/api/posts/";
    //     const { postId } = context.params as IParams;
    //     const res = await fetch(`${baseURL}${postId}`);
    //     const posts: PostsProps = await res.json();
    
    //     return{
    //         props: {
    //             posts
    //         }
    //     }
    // }
    
    export const getStaticProps: GetStaticProps = async (context) => {
        const queryClient = new QueryClient();
        const { postId } = context.params as IParams;
    
        await queryClient.prefetchQuery<PostsProps>(["post"], ()=> getPost(postId ? postId : 1));
        
        
        return {
            props: {
                dehydratedState: dehydrate(queryClient),
                
            }
        }
    }
    
    export const getStaticPaths: GetStaticPaths = async () => {
        const baseURL = "http://localhost:3000/api/posts/";
    
        const res = await fetch(`${baseURL}`);
        const posts: PostsProps[] = await res.json();
        
        const paths = posts.map((value)=>{
            return{
                params:{
                    postId: value.id.toString()
                }
            }
        })
    
        return {
            paths,
            fallback:false,
        };
    } 
     
    // export const getStaticProps: GetStaticProps<{ posts: PostsProps }> = async (context) => {
    // // export async function getStaticProps: GetStaticProps<{posts:PostsProps[]}>(){
        
    //     const baseURL = "http://localhost:3000/api/posts/";
    //     const { postId } = context.params as IParams;
    //     const res = await fetch(`${baseURL}${postId}`);
    //     const posts: PostsProps = await res.json();
        
    //     return{
    //         props: {
    //             posts
    //         }
    //     }
    // }
    
    // export const getStaticPaths: GetStaticPaths = async () => {
    //     const baseURL = "http://localhost:3000/api/posts/";
    
    //     const res = await fetch(`${baseURL}`);
    //     const posts: PostsProps[] = await res.json();
        
    //     const paths = posts.map((value)=>{
    //         return{
    //             params:{
    //                 postId: value.id.toString()
    //             }
    //         }
    //     })
    
    //     return {
    //         paths,
    //         fallback:false,
    //     };
    // }

     

     

    오류 디버깅

    error - unhandledRejection: TypeError: Cannot read properties of null (reading 'useRef')

    Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.

     

    Missing queryFn
    Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead

     

    Unhandled Runtime Error

    Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

     

     

    오류 원인 
    1. 아래 빨간색 오류는 부모 컴포넌트인 page 컴포넌트 화살표 함수에다가 async 쳐 박아서 에러남;; 
    2. 부모에 해당하는 페이지 컴포넌트 page()에다가 async 쳐박은 이유 react-query에 useQuery 훅에 2번째 인자값 getPost함수가 await 넣으라고 오류가 떠서 넣어서 

    Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.

     

    const getPost = async (postId:string | string[] | number) => await (await fetch(`http://localhost:3000/api/posts/${postId}`)).json();
    PostId에 따라 다이나믹 라우터로 게시글 가져오는 fetch함수인데 요놈 호출할때 await 대신 ()=>로 바꿔서 처리함.

     

    오류 코드

    const page = async() => {
        const router = useRouter();
        const { postId } = router.query;
        const { data, isLoading, isFetching } = useQuery<PostsProps>(["post", postId], await getPost(postId ? postId : 1));
        
    ...
    ...

    수정코드

    const page = () => {
        const router = useRouter();
        const { postId } = router.query;
        const { data, isLoading, isFetching } = useQuery<PostsProps>(["post", postId], () => getPost(postId ? postId : 1));

     

     

     

     

     

     

    https://hojung-testbench.tistory.com/entry/NextJs-React-Query%EB%A1%9C-ServrSide-Rendering-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0

     

    [NextJs] React-Query로 ServrSide Rendering 구현하기

    1. 무엇을 하려하는가? NextJS가 강력한 이유 중 하나는 SSR(server-side-Rendering)을 제공하기 때문이다. 서버 단에서 미리 데이터를 포함한 html파일을 렌더링한 후 프론트 쪽으로 보내주기 때문에 한 번

    hojung-testbench.tistory.com

     

     

    https://react-query-v3.tanstack.com/examples/nextjs

     

    React Query Nextjs Example | TanStack Query Docs

    An example showing how to implement Nextjs in React Query

    tanstack.com

     

    https://prateeksurana.me/blog/mastering-data-fetching-with-react-query-and-next-js/

     

    Mastering data fetching with React Query and Next.js

    Learn how React Query simplifies data fetching and caching for you and how it works in tandem with the Next.js pre-rendering methods

    prateeksurana.me

     

     

    728x90

    'Next.js' 카테고리의 다른 글

    Nextjs에서 Nestjs로 accessToken header로 요청할때 오류  (0) 2022.12.26
    Recoil, react-query 로그인 오류  (0) 2022.12.24
    [Nextjs]상태관리  (2) 2022.12.22
    [Nextjs 스와이프] Swiper 사용  (0) 2022.07.23
    [Nextjs 시작]  (0) 2022.07.19
Designed by Tistory.