React Router v7

了解 Sentry 的 React Router v7 集成。

将你的 Sentry.browserTracingIntegration 更新为 Sentry.reactRouterV7BrowserTracingIntegration,并提供所需的 React 钩子和路由器函数:

  • 来自 reactuseEffect 钩子
  • 来自 react-routeruseLocationuseNavigationType 钩子
  • 来自 react-routercreateRoutesFromChildrenmatchRoutes 函数

如果你选择使用 react-router 包中的 createBrowserRouter 创建路由器实例,可以使用 Sentry.wrapCreateBrowserRouterV7 将其包装以进行仪器化:

Copied
import React from "react";
import {
  createBrowserRouter,
  createRoutesFromChildren,
  matchRoutes,
  useLocation,
  useNavigationType,
} from "react-router";

import * as Sentry from "@sentry/react";

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    Sentry.reactRouterV7BrowserTracingIntegration({
      useEffect: React.useEffect,
      useLocation,
      useNavigationType,
      createRoutesFromChildren,
      matchRoutes,
    }),
  ],
  tracesSampleRate: 1.0,
});

const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV7(
  createBrowserRouter,
);

const router = sentryCreateBrowserRouter([
  // your routes...
]);

如果你使用 <Routes /> 组件定义路由,使用 Sentry.withSentryReactRouterV7Routing 包装 Routes。这将创建一个高阶组件,使 Sentry 能够访问你的路由器上下文。你也可以在 BrowserRouterMemoryRouterHashRouter 组件内部使用 Sentry.withSentryReactRouterV7Routing 来包装 Routes

Copied
import React from "react";
import ReactDOM from "react-dom";
import {
  Routes,
  Route,
  BrowserRouter,
  useLocation,
  useNavigationType,
  createRoutesFromChildren,
  matchRoutes,
} from "react-router";

import * as Sentry from "@sentry/react";

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    Sentry.reactRouterV7BrowserTracingIntegration({
      useEffect: React.useEffect,
      useLocation,
      useNavigationType,
      createRoutesFromChildren,
      matchRoutes,
    }),
  ],
  tracesSampleRate: 1.0,
});

const SentryRoutes = Sentry.withSentryReactRouterV7Routing(Routes);

ReactDOM.render(
  <BrowserRouter>
    <SentryRoutes>
      <Route path="/" element={<div>Home</div>} />
    </SentryRoutes>
  </BrowserRouter>,
);

这仅在应用的顶层需要,而不是像 v4/v5 那样需要包装每个你想要参数化的 <Route/>

如果你将路由定义作为对象传递给 useRoutes 钩子,使用 Sentry.wrapUseRoutesV7 创建一个修补过的 useRoutes 钩子,以便用 Sentry 仪器化你的路由。

Copied
import React from "react";
import {
  createRoutesFromChildren,
  matchRoutes,
  useLocation,
  useNavigationType,
  useRoutes,
} from "react-router";

import { wrapUseRoutes } from "@sentry/react";

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    Sentry.reactRouterV7BrowserTracingIntegration({
      useEffect: React.useEffect,
      useLocation,
      useNavigationType,
      createRoutesFromChildren,
      matchRoutes,
    }),
  ],
  tracesSampleRate: 1.0,
});

const useSentryRoutes = wrapUseRoutesV7(useRoutes);

function App() {
  return useSentryRoutes([
    // your routes...
  ]);
}

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root"),
);

现在,Sentry 应该生成带有参数化事务名称的 pageload/navigation 事务(例如,/teams/:teamid/user/:userid),在适用的情况下。这仅在应用的顶层需要,而不是像 v4/v5 那样需要包装每个你想要参数化的 <Route/>

当使用 react-router 时,在路由元素内部抛出的错误只会于 开发模式 下使用 strict mode 时重新抛出。在生产环境中,这些错误不会被暴露,除非手动捕获。如果你没有设置自定义错误边界,react-router 将创建一个默认的错误边界,该边界会“吞噬”所有错误。

注意,这仅适用于渲染方法和生命周期错误,因为 React 不需要错误边界来处理事件处理程序中的错误。

要在使用自定义错误边界时将错误发送到 Sentry,请使用 Sentry.captureException 方法:

Copied
// router setup
const sentryCreateBrowserRouter = wrapCreateBrowserRouterV7(createBrowserRouter);
const router = sentryCreateBrowserRouter([
  {
    path: "/",
    element: <YourLayout />,
    children: [
      {
        path: "",
        element: <Outlet />,
        errorElement: <YourCustomRootErrorBoundary />,
        children: [
          // other routes ... 
        ],
      },
    ],
  },
]);

// error boundary
import { useRouteError } from "react-router-dom";
import * as Sentry from "@sentry/react";

export function YourCustomRootErrorBoundary() {
  const error = useRouteError() as Error;

  React.useEffect(() => {
    Sentry.captureException(error);
  }, [error]);

  return (
    <div>
      <h1>Ouch!</h1>
    </div>
  );
}