如何访问React Router v6.4 actionsloaders中的Auth0访问令牌,以便调用安全的API?

我试图将getAccessTokenSilently()方法指向加载器,但我不能在组件外部调用useAuth0()钩子(我的路径是在组件as recommended by the react router documentation外部创建的).出于同样的原因,我不能在加载器中调用useAuth0():它不是一个组件.

我try 将路由放在组件中(将下面代码中显示的路由移动到<App/>组件中),但这导致了useAuth0()中的错误,即必须从Auth0Provider中调用它.

官方的Auth0 guide to React Router v6还没有更新到使用createBrowserRouter,在他们的安全API示例中也没有使用加载器.

这似乎是任何Reaction RouterV6.4应用程序的基本需求,但我从来没有找到一个示例来说明如何做到这一点.以前肯定有人这么做过吧?

Update: Thanks to the assistance of Drew this works! The below code is a full working version of this.

Auth0ProviderWithNavigate.tsx

interface Auth0ProviderWithNavigateProps {
  children: React.ReactNode;
}

export const Auth0ProviderWithNavigate = ({
  children,
}: Auth0ProviderWithNavigateProps) => {
  if (
    !(
      ENV.VITE_AUTH0_DOMAIN &&
      ENV.VITE_AUTH0_CLIENT_ID &&
      ENV.VITE_AUTH0_CALLBACK_URL
    )
  ) {
    return null;
  }

  return (
    <Auth0Provider
      domain={ENV.VITE_AUTH0_DOMAIN}
      clientId={ENV.VITE_AUTH0_CLIENT_ID}
      authorizationParams={{
        redirect_uri: ENV.VITE_AUTH0_CALLBACK_URL,
      }}
    >
      {children}
    </Auth0Provider>
  );
};

AuthenticationGuard.tsx

import { withAuthenticationRequired } from "@auth0/auth0-react";
import { PageLoader } from "./PageLoader";

interface AuthenticationGuardProps {
  component: React.ComponentType<object>;
}

export const AuthenticationGuard = ({
  component,
}: AuthenticationGuardProps) => {
  const Component = withAuthenticationRequired(component, {
    onRedirecting: () => <PageLoader />,
  });

  return <Component />;
};

main.tsx

const root = document.getElementById("root");

root &&
  ReactDOM.createRoot(root).render(
    <React.StrictMode>
      <AppContextProvider>
        <Auth0ProviderWithNavigate>
          <AppTheme>
            <App />
          </AppTheme>
        </Auth0ProviderWithNavigate>
      </AppContextProvider>
    </React.StrictMode>
  );

App.tsx

const App = () => {
  const { getAccessTokenSilently } = useAuth0();

  const router = useMemo(() => {
    return createBrowserRouter([
      {
        children: [
          {
            path: "/",
            element: <WelcomePage />,
            errorElement: <ErrorPage />,
          },
          {
            children: [
              {
                id: "edct",
                path: "/edct",
                element: <AuthenticationGuard component={Edct} />,
                errorElement: <ErrorPage />,
                loader: AirportCodesLoader({ getAccessTokenSilently }),
              },
            ],
          },
        ],
      },
    ]);
  }, [getAccessTokenSilently]);

  return <RouterProvider router={router} />;
};

export default App;

AirportCodesLoader.tsx

import { ActionFunction } from "react-router";

const cleanCodes = (codes: string | null): string | null => {
  if (!codes) {
    return null;
  }
  return codes
    .split(",")
    .map((code) => code.trim())
    .join(",");
};

interface AppAction {
  getAccessTokenSilently: () => Promise<string>;
}

export const AirportCodesLoader =
  ({ getAccessTokenSilently }: AppAction): ActionFunction =>
  async ({ request }) => {
    const url = new URL(request.url);

    console.log(await getAccessTokenSilently());
    return {
      departureCodes: cleanCodes(url.searchParams.get("departureCodes")) ?? "",
      arrivalCodes: cleanCodes(url.searchParams.get("arrivalCodes")) ?? "",
    };
  };

推荐答案

是的,虽然在react 树外部声明路由配置是常见的做法,但这只是一个建议.在本例中,您可以在App组件中创建它,这样它就可以关闭auth0上下文值的一个实例,并将其传递给路由加载器.需要接收auth0上下文的路由加载器将需要使用该参数.

示例:

const AirportCodesLoader = (auth0) => ({ params, request }) => {
  ... loader logic ...
};
const App = () => {
  const auth0 = useAuth0();

  const router = React.useMemo(() => { // <-- memoize router reference
    return createBrowserRouter([
      {
        children: [
          {
            path: "/",
            element: <WelcomePage />,
            errorElement: <ErrorPage />,
          },
          {
            children: [
              {
                id: "edct",
                path: "/edct",
                element: <AuthenticationGuard component={Edct} />,
                errorElement: <ErrorPage />,
                loader: AirportCodesLoader(auth0), // <-- pass auth0 to loader
              },
            ],
          },
        ],
      },
    ]);
  }, [auth0]); // <-- auth0 external dependency

  return <RouterProvider router={router} />;
};

Typescript相关问答推荐

TS 2339:属性切片不存在于类型DeliverableSignal产品[]上>'

TypScript如何在 struct 上定义基元类型?

我们过go 不能从TypeScript中的联合中同时访问所有属性?

将值添加到具有不同类型的对象的元素

contextPaneTitleText类型的参数不能分配给remoteconfig类型的关键参数'""'''

如何在TypeScrip中使用嵌套对象中的类型映射?

如何按不能保证的功能过滤按键?

如何将所有props传递给React中的组件?

为什么&;(交集)运算符会删除不相交的属性?

如何在ANGLE中注册自定义验证器

如何编写在返回函数上分配了属性的类型安全泛型闭包

TypeScrip中的类型安全包装类

为什么Typescript只在调用方提供显式泛型类型参数时推断此函数的返回类型?

如何在脚本中输入具有不同数量的泛型类型变量的函数?

在打字脚本中对可迭代对象进行可变压缩

映射类型不是数组类型

如何键入';v型';

在 next.js 13.4 中为react-email-editor使用forwardRef和next/dynamic的正确方法

使用 Next.js 路由重定向到原始 URL 不起作用

Next.JS-13(应用程序路由)中发现无效的中间件