Remix 앱에 전역 진행률 표시기 추가

이번 포스팅은 표지 이미지 좌측 하단에 있는 스피너에 대한 내용입니다 🤓

면책 조항: 이 게시물에 대한 후속 게시물이 있습니다.



나중에 확인하세요.

소개



Seasoned에서 우리는 항상 better UX/DX with less code을 제공할 방법을 찾고 있으며 이것이 우리가 Remix에 많은 투자를 해온 주된 이유 중 하나입니다.

Remix 앱에 최근 추가된 매우 멋진 기능 중 하나는 전역 진행률 표시기였습니다. Remix의 Form 및 Fetcher를 활용함으로써 우리는 브라우저가 서버로 수행할 수 있는 모든 왕복에 대한 단일 정보 소스를 갖게 됩니다.

따라서 app/root.tsx 파일에 약간의 구성 요소를 놓으면 미묘한 진행률 표시기를 표시할 수 있습니다. "SPA 시대"에 익숙해진 눈에 띄는 스피너에 대해 말하는 것이 아니라 브라우저의 기본 탭 스피너 - 페이지가 로드/새로고침될 때마다.



알겠습니다. 코드를 보여주세요!



준비



이 연습에서 사용할 것이므로 이미 Indie Stack이 설치된 tailwind을 사용하여 새 Remix 앱을 부트스트랩했습니다.

리포지토리를 만든 후 구성 클래스를 추가하고 스피너를 로컬에서 더 잘 테스트할 수 있도록 도우미sleep도 만들었습니다.

// app/utils.ts
const sleep = (time: number) =>
  new Promise((resolve) => setTimeout(resolve, time));

export { sleep }


느린 백엔드 활동을 시뮬레이트하려는 위치에서 sleep 메서드 사용:

// Add this to any loaders and actions
export async function loader({ request }: LoaderArgs) {
  await sleep(1000);
  return json({
    // ..
  });
}

export async function action({ request }: ActionArgs) {
  await sleep(1000);
  return json({
    // ..
  });
}


GlobalLoading 구성 요소



여기에 구성 요소 코드를 추가하고 나중에 중요한 부분을 설명하겠습니다.

import { useTransition } from "@remix-run/react";
import { cx } from "~/utils";

function GlobalLoading() {
  const transition = useTransition();
  const active = transition.state !== "idle";

  return (
    <div
      role="progressbar"
      aria-valuetext={active ? "Loading" : undefined}
      aria-hidden={!active}
      className={cx(
        "pointer-events-none fixed left-0 bottom-0 z-50 p-4 transition-all duration-500 ease-out",
        active ? "translate-y-0" : "translate-y-full"
      )}
    >
      <svg
        className="h-7 w-7 animate-spin"
        xmlns="http://www.w3.org/2000/svg"
        fill="none"
        viewBox="0 0 24 24"
        width="1em"
        height="1em"
      >
        <circle
          className="stroke-blue-600/25"
          cx={12}
          cy={12}
          r={10}
          strokeWidth={4}
        />
        <path
          className="fill-blue-600"
          d="M4 12a8 8 0 0 1 8-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 0 1 4 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
        />
      </svg>
    </div>
  );
}

export { GlobalLoading };


스피너



우선, SVG는 그들의 문서에서 "공식"Tailwind spinner이며 특별한 것은 없습니다.

활성 상태



이것은 쉬워요. Remix와 그들의 useTransition hook 덕분에 transition.state !== 'idle'가 있을 때마다 로더/액션 유효성 검사가 있음을 알 수 있습니다.

아리아-* 물건


role="progressbar" 상태에 기반한 값으로 aria-valuetext , aria-hiddenactive 를 추가하여 앱을 검사할 수 있는 모든 a11y 장치에 일부 의미 체계를 가져올 수 있습니다.

Tailwind 클래스



구성 요소 기능의 핵심은 tailwind 클래스에 있습니다.

className={cx(
  "pointer-events-none fixed left-0 bottom-0 z-50 p-4 transition-all duration-500 ease-out",
  active ? "translate-y-0" : "translate-y-full"
)}


물론 이를 위해 TW가 꼭 필요한 것은 아니지만, 여러분이 그것에 대해 조금 알고 있다고 가정하겠습니다.
  • pointer-events-none fixed left-0 bottom-0 z-50 : 우리는 그것이 앱의 왼쪽 하단 모서리에 있고 모든 콘텐츠 위에 떠 있지만 페이지의 나머지 부분과의 마우스/터치 상호 작용을 차단하지 않기를 원합니다.
  • transition-all duration-500 ease-out : 스피너의 시작 및 사라짐을 애니메이션화합니다.
  • active ? "translate-y-0" : "translate-y-full" : 스피너가 활성화되면 원래 위치에 표시되고, 그렇지 않으면 스피너 컨테이너의 크기와 동일한 거리에서 Y축으로 아래로 이동합니다
  • .

    엣 짜잔



    이제 이 구성 요소를 한 번만 가져와서 추가하면 됩니다.

    // app/root.tsx
    import { GlobalLoading } from "./components/global-loading";
    
    export default function App() {
      return (
        <html lang="en" className="h-full">
          <head />
          <body className="h-full">
            <GlobalLoading />
            <Outlet />
            <ScrollRestoration />
            <Scripts />
            <LiveReload />
          </body>
        </html>
      );
    }
    


    이 구성 요소는 매우 단순하지만 앱의 전체 UX를 약간 향상시킵니다. 앱의 모든 기능을 점진적으로 개선할 시간이 없거나 잊어버리더라도 기본 UX는 견고하다는 것을 알고 있으므로 다시 생각할 필요가 없습니다.

    그것이 바로 우리가 Remix에 대해 좋아하는 것입니다.

    우리는 더 깊이 잠수할 수 있습니다



    다음 게시물에서는 이 주제를 확장하여 Github와 유사한 진행률 표시줄을 만들 수 있습니다.

    좋은 웹페이지 즐겨찾기