그럼에도 불구하고

👨‍💻

[Router] useFetcher 본문

React/Router

[Router] useFetcher

zenghyun 2023. 6. 12. 14:54

 

useFetcher에 대해 알아보겠습니다.

 

 

[ useFetcher ]

HTML/HTTP에서 데이터 변형 및 로드의 경우 <a href> 또는 <form action> 탐색으로 모델링 됩니다. 둘 다 브라우저에서 탐색을 유발하며, 이는 React Router에서 <Link>와 <Form>에 해당합니다.

 

그러나 때로는 탐색 외부에서 로더를 호출하거나 URL을 변경하지 않고 작업을 호출(혹은 유효성을 다시 검사할 페이지의 데이터 가져오기)을 원할 수 있습니다. 또는 동시에 여러 변이를 수행해야 할 수 도 있습니다. 

 

이럴 때 useFetcher 훅을 사용하면 UI를 작업 및  로더에 연결할 수 있습니다.

 

이 기능은 데이터 라우터를 사용하는 경우에만 작동합니다. 

 

그리고 useFetcher는 다음과 같은 경우에 유용합니다.

 

1. UI 경로와 연결되지 않은 데이터 가져오기 (팝오버, 동적 양식 등)

 

2. 탐색하지 않고 작업에 데이터 제출하기 (뉴스레터 가입과 같은 공유 구성 요소)

 

3. 목록에서 여러 동시 제출 처리 (여러 버튼을 클릭할 수 있고 모두 동시에 보류되어야 하는 일반적인 "todo 앱" 목록) 

 

4. 고도의 대화형 "앱과 같은" 사용자 인터페이스를 구축하는 Fetcher 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { useFetcher } from "react-router-dom";
 
function SomeComponent() {
  const fetcher = useFetcher();
 
  // call submit or load in a useEffect
  React.useEffect(() => {
    fetcher.submit(data, options);
    fetcher.load(href);
  }, [fetcher]);
 
  // build your UI with these properties
  fetcher.state;
  fetcher.formData;
  fetcher.formMethod;
  fetcher.formAction;
  fetcher.data;
 
  // render a form that doesn't cause navigation
  return <fetcher.Form />;
}
 
cs

 

Fetcher에는 많은 기본 제공 동작이 있습니다. 

 

1. 가져오기 중단 시 자동으로 취소 처리

 

2. POST, PUT, PATCH, DELETE로 제출할 때 액션이 먼저 호출됩니다. 작업이 완료되면 발생했을 수 있는 변형을 감지하기 위해 페이지의 데이터가 재검증되어 UI가 서버 상태와 자동으로 동기화됩니다.

 

3. 여러 개의 fetcher가 한 번에 실행 중인 경우 도착할 때마다 사용 가능한 최신 데이터를 commit 합니다.

 

4. 응답이 반환되는 순서에 관계없이 오래된 로드가 최신 데이터를 재정의 하지 않도록 합니다.

 

5. 가장 가까운 errorElement를 렌더링 하여 포착되지 않은 오류를 처리합니다. ( <Link> 또는 <Form>의 일반 탐색과 동일 )

 

6. 호출 중인 작업/로더가 redirect를 반환하는 경우 앱을 redirect 합니다.  ( <Link> 또는 <Form>의 일반 탐색과 동일 )

 

 

[ fetcher.state ]

fetcher.state를 통해 fetcher의 상태를 알 수 있습니다. 

 

📌 idle: 아무것도 가져오지 않습니다.

 

📌 submitting: POST, PUT, PATCH 또는  DELETE를 사용한 fetcher 제출로 인해 라우팅 작업이 호출되고 있습니다. 

 

📌 loading: fetcher가 (fetcher.load에서) loader를 호출하거나 별도의 제출 도는 useRevaildator 호출 후 재검증 중입니다.

 

[ fetcher.Form ]

navigation을 일으키지 않는다는 점을 제외하면 <Form>과 같습니다.

 

1
2
3
4
5
6
7
8
function SomeComponent() {
  const fetcher = useFetcher();
  return (
    <fetcher.Form method="post" action="/some/route">
      <input type="text" />
    </fetcher.Form>
  );
}
cs

 

[ fetcher.load() ]

routerloader에서 date를 load합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
import { useFetcher } from "react-router-dom";
 
function SomeComponent() {
  const fetcher = useFetcher();
 
  useEffect(() => {
    if (fetcher.state === "idle" && !fetcher.data) {
      fetcher.load("/some/route");
    }
  }, [fetcher]);
 
  return <div>{fetcher.data || "Loading..."}</div>;
}
cs

 

URL이 여러 중첩된 경로와 일치할 수 있지만 fetcher.load() 호출은 leaf match(또는 인덱스 경로의 부모)에서만 로더를 호출합니다.

 

만약, click handler 내에서 이 함수를 호출하는 경우 <fetcher.Form>을 대신 사용하여 코드를 단순화할 수 있습니다. 

 

 

※ Note

페이지에서 활성 상태인 모든 fetcher.load 호출은 유효성 재검사의 일부로 다시 실행됩니다. (탐색 제출, 또는 다른 가져오기 제출 또는 useRevalidator() 호출 후 )

 

[ fetcher.submit ]

fetcher.submit은 <fetcher.Form>의 명령형 버전입니다. 사용자 상호 작용이 가져오기를 시작해야 하는 경우 <fetcher.Form>을 사용해야 합니다. 그러나 프로그래머가 가져오기를 시작하는 경우 이 기능을 사용할 수 있습니다. 

 

예를 들어, 특정 시간이 지난 후 사용자를 로그아웃 시킬 수 있습니다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { useFetcher } from "react-router-dom";
import { useFakeUserIsIdle } from "./fake/hooks";
 
export function useIdleLogout() {
  const fetcher = useFetcher();
  const userIsIdle = useFakeUserIsIdle();
 
  useEffect(() => {
    if (userIsIdle) {
      fetcher.submit(
        { idle: true },
        { method: "post", action: "/logout" }
      );
    }
  }, [userIsIdle]);
}
cs

 

[ fetcher.data ]

loader 또는 작업에서 반환된 데이터가 여기에 저장됩니다. 데이터가 설정되면 다시 로드하고 다시 제출해도 fetcher에서 유지됩니다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function ProductDetails({ product }) {
  const fetcher = useFetcher();
 
  return (
    <details
      onToggle={(event=> {
        if (
          event.currentTarget.open &&
          fetcher.state === "idle" &&
          !fetcher.data
        ) {
          fetcher.load(`/product/${product.id}/details`);
        }
      }}
    >
      <summary>{product.name}</summary>
      {fetcher.data ? (
        <div>{fetcher.data}</div>
      ) : (
        <div>Loading product details...</div>
      )}
    </details>
  );
}
cs

 

[ fetcher.formData ]

<fetcher.Form> 또는 fetcher.submit()을 사용할 때 양식 데이터를 사용하여 낙관적인 UI를 구축할 수 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function TaskCheckbox({ task }) {
  let fetcher = useFetcher();
 
  // while data is in flight, use that to immediately render
  // the state you expect the task to be in when the form
  // submission completes, instead of waiting for the
  // network to respond. When the network responds, the
  // formData will no longer be available and the UI will
  // use the value in `task.status` from the revalidation
  let status =
    fetcher.formData?.get("status"|| task.status;
 
  let isComplete = status === "complete";
 
  return (
    <fetcher.Form method="post">
      <button
        type="submit"
        name="status"
        value={isComplete ? "complete" : "incomplete"}
      >
        {isComplete ? "Mark Complete" : "Mark Incomplete"}
      </button>
    </fetcher.Form>
  );
}
cs

 

[ fetcher.formAction ]

양식이 제출되는 작업 URL을 알려줍니다.

 

1
2
3
4
<fetcher.Form action="/mark-as-read" />;
 
// when the form is submitting
fetcher.formAction; // "mark-as-read"
cs

 

[ fetcher.formMethod ]

제출되는 양식의 방법(get, post, put, patch, delete)을 알려줍니다.

 

1
2
3
4
<fetcher.Form method="post" />;
 
// when the form is submitting
fetcher.formMethod; // "post"
cs

 

 

ref: https://reactrouter.com/en/main/hooks/use-fetcher#fetchersubmit

Comments