본문 바로가기
코딩 정보/React

[React][Router] 라우터 활용을 통한 비동기 통신 (하)

by 꽁이꽁설꽁돌 2024. 8. 22.
728x90
반응형

목차

     

    참고

    전편을 보고 보면 이해에 도움이 됩니다.

    https://be-senior-developer.tistory.com/175

     

    [React][Router] 라우터 활용을 통한 비동기 통신 (상)

    목차 lodaer활용하기우리는 페이지를 불러오기 전에 데이터를 먼저 불러오고 그 데이터를 기반으로 페이지를 불러오고 싶은 경우가 있다.그럴떄 loader를 이용하면 된다. import { createBrowserRouter, Ro

    be-senior-developer.tistory.com

     

    defer과 Await, Suspense를 이용한 로딩화면 개선

    아래와 같이 데이터가 불러오기전에 네비게이션을 보여주여 사용자의 경험 향상을 시키고 싶을 때가 있다.

     

    import EventsList from "../src/components/EventsList";
    import { Suspense } from "react";
    import { useLoaderData, json, defer, Await } from "react-router-dom";
    function EventsPages() {
      const { events } = useLoaderData();
    
      return (
        //suspense를 통해 fallback상태에 보여지는 컴포너트 설정
        <Suspense fallback={<p style={{ textAlign: "center" }}>Loading...</p>}>
          {/* Await를 통해 받아들이는 event를 설정하고 받은 이벤트로 랜더링 하게끔 만듬 */}
          <Await resolve={events}>
            {(loadedEvents) => <EventsList events={loadedEvents}></EventsList>}
          </Await>
        </Suspense>
      );
    }
    
    export default EventsPages;
    
    async function loadEvents() {
      const response = await fetch("http://localhost:8080/events");
    
      if (!response.ok) {
        console.log(
          json(
            { message: "Could not fetch events" },
            {
              status: 500,
            }
          )
        );
        throw json(
          { message: "Could not fetch events" },
          {
            status: 500,
          }
        );
      } else {
        //defer를 거치기 때문에 직접 변환 시켜서 반환해야함
        //JSON 본문을 자바스크립트 객체로 변환
        const resData = await response.json();
        return resData.events;
      }
    }
    
    export function loader() {
      return defer({
        events: loadEvents(),
      });
    }

     

    여러개의 컴포넌트 데이터를 가져올 때 로딩 페이지 핸들링하기

    import EventItem from "../src/components/EventItem";
    import { Suspense } from "react";
    import {
      json,
      redirect,
      useRouteLoaderData,
      Await,
      defer,
    } from "react-router-dom";
    import EventsList from "../src/components/EventsList";
    function EventDetailPage() {
      const { event, events } = useRouteLoaderData("event-detail");
    
      return (
        <>
          <Suspense fallback={<p style={{ textAlign: "center" }}>loading...</p>}>
            <Await resolve={event}>
              {(loadedEvent) => <EventItem event={loadedEvent}></EventItem>}
            </Await>
          </Suspense>
    
          <Suspense fallback={<p style={{ textAlign: "center" }}>loading...</p>}>
            <Await resolve={events}>
              {(loadedEvents) => <EventsList events={loadedEvents}></EventsList>}
            </Await>
          </Suspense>
        </>
      );
    }
    
    export default EventDetailPage;
    
    async function loadEvent(id) {
      const response = await fetch(`http://localhost:8080/events/` + id);
      if (!response.ok) {
        throw json(
          { msessage: "Could not fetch details for selected event." },
          {
            status: 500,
          }
        );
      } else {
        const resData = await response.json();
        return resData.event;
      }
    }
    
    async function loadEvents() {
      const response = await fetch("http://localhost:8080/events");
    
      if (!response.ok) {
        console.log(
          json(
            { message: "Could not fetch events" },
            {
              status: 500,
            }
          )
        );
        throw json(
          { message: "Could not fetch events" },
          {
            status: 500,
          }
        );
      } else {
        //defer를 거치기 때문에 직접 변환 시켜서 반환해야함
        //JSON 본문을 자바스크립트 객체로 변환
        const resData = await response.json();
        return resData.events;
      }
    }
    
    //요청 객체와 parmas를 포함하고 있음
    export async function loader({ request, params }) {
      const id = params.eventId;
      
      //이런식으로 await를 통해 먼저 로딩해야할 것을 정해 줄 수 있다.
      return defer({
        event: await loadEvent(id),
        events: loadEvents(),
      });
    }
    
    export async function action({ request, params }) {
      const Id = params.eventId;
      const response = await fetch(`http://localhost:8080/events/` + Id, {
        method: request.method,
      });
      if (!response.ok) {
        throw json(
          { msessage: "Could not fetch details for selected event." },
          {
            status: 500,
          }
        );
      } else {
        return redirect("/events");
      }
    }

     

    먼저 로딩이 된 후 그 아래 컴포넌트가 로딩된다.

     

    useFetcher를 통한 페이지를 이동하지 않고 데이터 이용하기

    우리는 종종 공통된 컴포넌트를 쓰거나 같은 페이지에서 여러번 쓰는 컴포넌트가 있을 수 있다.

    그럴때 이동하지 않고 데이터를 받아오기 위해 useFetcher를 사용한다.

    import { useEffect } from "react";
    import classes from "./NewsLetterSignup.module.css";
    import { useFetcher } from "react-router-dom";
    function NewsLetterSignup() {
        // 공통된 컴포넌트가 있겅나 같은 페이지에서 여러번 사용되는 
        // 컴포넌트가 있을 경우에 데이터 받을 경우 유용함
          const fetcher = useFetcher();
          
      const { data, state } = fetcher;
      useEffect(() => {
        if (state === "idle" && data && data.message) {
          window.alert(data.message);
        }
      }, [data, state]);
      return (
        //fetcher.Form은 다른 라우트로의 이동을 막아준다.
        <fetcher.Form
          method="post"
          action="/newsletter"
          className={classes.newsletter}
        >
          <input
            type="email"
            placeholder="Sign up for newsletter..."
            aria-label="Sign up for newsletter"
          />
          <button>Sign up</button>
        </fetcher.Form>
      );
    }
    
    export default NewsLetterSignup;
    반응형