Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/small-flowers-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-router": minor
---

Stabilize `<HydratedRouter onError>`/`<RouterProvider onError>`
2 changes: 1 addition & 1 deletion integration/browser-entry-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ test("allows users to pass an onError function to HydratedRouter", async ({
document,
<StrictMode>
<HydratedRouter
unstable_onError={(error, errorInfo) => {
onError={(error, errorInfo) => {
console.log(error.message, JSON.stringify(errorInfo))
}}
/>
Expand Down
30 changes: 15 additions & 15 deletions packages/react-router/__tests__/dom/client-on-error-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe(`handleError`, () => {
]);

let { container } = render(
<RouterProvider router={router} unstable_onError={spy} />,
<RouterProvider router={router} onError={spy} />,
);
await waitFor(() => screen.getByText("lazy error!"));

Expand Down Expand Up @@ -74,7 +74,7 @@ describe(`handleError`, () => {
},
]);

render(<RouterProvider router={router} unstable_onError={spy} />);
render(<RouterProvider router={router} onError={spy} />);

await waitFor(() => screen.getByText("Error:middleware error!"));

Expand Down Expand Up @@ -102,7 +102,7 @@ describe(`handleError`, () => {
},
]);

render(<RouterProvider router={router} unstable_onError={spy} />);
render(<RouterProvider router={router} onError={spy} />);

await waitFor(() => screen.getByText("Error:loader error!"));

Expand Down Expand Up @@ -131,7 +131,7 @@ describe(`handleError`, () => {
]);

let { container } = render(
<RouterProvider router={router} unstable_onError={spy} />,
<RouterProvider router={router} onError={spy} />,
);

await act(() => router.navigate("/page"));
Expand Down Expand Up @@ -166,7 +166,7 @@ describe(`handleError`, () => {
]);

let { container } = render(
<RouterProvider router={router} unstable_onError={spy} />,
<RouterProvider router={router} onError={spy} />,
);

await act(() => router.navigate("/page"));
Expand Down Expand Up @@ -197,7 +197,7 @@ describe(`handleError`, () => {
]);

let { container } = render(
<RouterProvider router={router} unstable_onError={spy} />,
<RouterProvider router={router} onError={spy} />,
);

await act(() => router.navigate("/page"));
Expand Down Expand Up @@ -228,7 +228,7 @@ describe(`handleError`, () => {
]);

let { container } = render(
<RouterProvider router={router} unstable_onError={spy} />,
<RouterProvider router={router} onError={spy} />,
);

await act(() =>
Expand Down Expand Up @@ -262,7 +262,7 @@ describe(`handleError`, () => {
]);

let { container } = render(
<RouterProvider router={router} unstable_onError={spy} />,
<RouterProvider router={router} onError={spy} />,
);

await act(() => router.fetch("key", "0", "/fetch"));
Expand Down Expand Up @@ -291,7 +291,7 @@ describe(`handleError`, () => {
]);

let { container } = render(
<RouterProvider router={router} unstable_onError={spy} />,
<RouterProvider router={router} onError={spy} />,
);

await act(() =>
Expand Down Expand Up @@ -326,7 +326,7 @@ describe(`handleError`, () => {
]);

let { container } = render(
<RouterProvider router={router} unstable_onError={spy} />,
<RouterProvider router={router} onError={spy} />,
);

await act(() => router.navigate("/page"));
Expand Down Expand Up @@ -370,7 +370,7 @@ describe(`handleError`, () => {
]);

let { container } = render(
<RouterProvider router={router} unstable_onError={spy} />,
<RouterProvider router={router} onError={spy} />,
);

await act(() => router.navigate("/page"));
Expand Down Expand Up @@ -418,7 +418,7 @@ describe(`handleError`, () => {
}

let { container } = render(
<RouterProvider router={router} unstable_onError={spy} />,
<RouterProvider router={router} onError={spy} />,
);

await act(() => router.navigate("/page"));
Expand Down Expand Up @@ -472,7 +472,7 @@ describe(`handleError`, () => {
}

let { container } = render(
<RouterProvider router={router} unstable_onError={spy} />,
<RouterProvider router={router} onError={spy} />,
);

await act(() => router.navigate("/page"));
Expand Down Expand Up @@ -526,7 +526,7 @@ describe(`handleError`, () => {
]);

let { container } = render(
<RouterProvider router={router} unstable_onError={spy} />,
<RouterProvider router={router} onError={spy} />,
);

await act(() => router.navigate("/page"));
Expand Down Expand Up @@ -576,7 +576,7 @@ describe(`handleError`, () => {
]);

let { container } = render(
<RouterProvider router={router} unstable_onError={spy} />,
<RouterProvider router={router} onError={spy} />,
);

await act(() => router.navigate("/page"));
Expand Down
38 changes: 19 additions & 19 deletions packages/react-router/lib/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -347,13 +347,13 @@ export interface RouterProviderProps {
* and is only present for render errors.
*
* ```tsx
* <RouterProvider unstable_onError=(error, errorInfo) => {
* <RouterProvider onError=(error, errorInfo) => {
* console.error(error, errorInfo);
* reportToErrorService(error, errorInfo);
* }} />
* ```
*/
unstable_onError?: unstable_ClientOnErrorFunction;
onError?: unstable_ClientOnErrorFunction;
}

function shallowDiff(a: any, b: any) {
Expand All @@ -376,7 +376,7 @@ function shallowDiff(a: any, b: any) {
export function UNSTABLE_TransitionEnabledRouterProvider({
router,
flushSync: reactDomFlushSyncImpl,
unstable_onError,
onError,
}: RouterProviderProps) {
let fetcherData = React.useRef<Map<string, any>>(new Map());
let [revalidating, startRevalidation] = React.useTransition();
Expand Down Expand Up @@ -420,9 +420,9 @@ export function UNSTABLE_TransitionEnabledRouterProvider({
navigator,
static: false,
basename,
unstable_onError,
onError,
}),
[router, navigator, basename, unstable_onError],
[router, navigator, basename, onError],
);

React.useLayoutEffect(() => {
Expand Down Expand Up @@ -481,7 +481,7 @@ export function UNSTABLE_TransitionEnabledRouterProvider({
routes={router.routes}
future={router.future}
state={state}
unstable_onError={unstable_onError}
onError={onError}
/>
</Router>
{/* </ViewTransitionContext.Provider> */}
Expand Down Expand Up @@ -520,14 +520,14 @@ export function UNSTABLE_TransitionEnabledRouterProvider({
* @mode data
* @param props Props
* @param {RouterProviderProps.flushSync} props.flushSync n/a
* @param {RouterProviderProps.unstable_onError} props.unstable_onError n/a
* @param {RouterProviderProps.onError} props.onError n/a
* @param {RouterProviderProps.router} props.router n/a
* @returns React element for the rendered router
*/
export function RouterProvider({
router,
flushSync: reactDomFlushSyncImpl,
unstable_onError,
onError,
}: RouterProviderProps): React.ReactElement {
let [state, setStateImpl] = React.useState(router.state);
let [pendingState, setPendingState] = React.useState<RouterState>();
Expand All @@ -546,10 +546,10 @@ export function RouterProvider({
(newState: RouterState) => {
setStateImpl((prevState) => {
// Send loader/action errors through handleError
if (newState.errors && unstable_onError) {
if (newState.errors && onError) {
Object.entries(newState.errors).forEach(([routeId, error]) => {
if (prevState.errors?.[routeId] !== error) {
unstable_onError(error, {
onError(error, {
location: newState.location,
params: newState.matches[0]?.params ?? {},
});
Expand All @@ -559,7 +559,7 @@ export function RouterProvider({
return newState;
});
},
[unstable_onError],
[onError],
);

let setState = React.useCallback<RouterSubscriber>(
Expand Down Expand Up @@ -761,9 +761,9 @@ export function RouterProvider({
navigator,
static: false,
basename,
unstable_onError,
onError,
}),
[router, navigator, basename, unstable_onError],
[router, navigator, basename, onError],
);

// The fragment and {null} here are important! We need them to keep React 18's
Expand All @@ -788,7 +788,7 @@ export function RouterProvider({
routes={router.routes}
future={router.future}
state={state}
unstable_onError={unstable_onError}
onError={onError}
/>
</Router>
</ViewTransitionContext.Provider>
Expand All @@ -807,14 +807,14 @@ function DataRoutes({
routes,
future,
state,
unstable_onError,
onError,
}: {
routes: DataRouteObject[];
future: DataRouter["future"];
state: RouterState;
unstable_onError: unstable_ClientOnErrorFunction | undefined;
onError: unstable_ClientOnErrorFunction | undefined;
}): React.ReactElement | null {
return useRoutesImpl(routes, undefined, state, unstable_onError, future);
return useRoutesImpl(routes, undefined, state, onError, future);
}

/**
Expand Down Expand Up @@ -1614,10 +1614,10 @@ export function Await<Resolve>({
(error: unknown, errorInfo?: React.ErrorInfo) => {
if (
dataRouterContext &&
dataRouterContext.unstable_onError &&
dataRouterContext.onError &&
dataRouterStateContext
) {
dataRouterContext.unstable_onError(error, {
dataRouterContext.onError(error, {
location: dataRouterStateContext.location,
params: dataRouterStateContext.matches?.[0]?.params || {},
errorInfo,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-router/lib/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export interface DataRouterContextObject
extends Omit<NavigationContextObject, "future"> {
router: Router;
staticContext?: StaticHandlerContext;
unstable_onError?: unstable_ClientOnErrorFunction;
onError?: unstable_ClientOnErrorFunction;
}

export const DataRouterContext =
Expand Down
11 changes: 4 additions & 7 deletions packages/react-router/lib/dom-export/hydrated-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -291,13 +291,13 @@ export interface HydratedRouterProps {
* and is only present for render errors.
*
* ```tsx
* <HydratedRouter unstable_onError={(error, errorInfo) => {
* <HydratedRouter onError={(error, errorInfo) => {
* console.error(error, errorInfo);
* reportToErrorService(error, errorInfo);
* }} />
* ```
*/
unstable_onError?: unstable_ClientOnErrorFunction;
onError?: unstable_ClientOnErrorFunction;
}

/**
Expand All @@ -309,7 +309,7 @@ export interface HydratedRouterProps {
* @mode framework
* @param props Props
* @param {dom.HydratedRouterProps.getContext} props.getContext n/a
* @param {dom.HydratedRouterProps.unstable_onError} props.unstable_onError n/a
* @param {dom.HydratedRouterProps.onError} props.onError n/a
* @returns A React element that represents the hydrated application.
*/
export function HydratedRouter(props: HydratedRouterProps) {
Expand Down Expand Up @@ -402,10 +402,7 @@ export function HydratedRouter(props: HydratedRouterProps) {
}}
>
<RemixErrorBoundary location={location}>
<RouterProvider
router={router}
unstable_onError={props.unstable_onError}
/>
<RouterProvider router={router} onError={props.onError} />
</RemixErrorBoundary>
</FrameworkContext.Provider>
{/*
Expand Down
10 changes: 5 additions & 5 deletions packages/react-router/lib/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ export function useRoutesImpl(
routes: RouteObject[],
locationArg?: Partial<Location> | string,
dataRouterState?: DataRouter["state"],
unstable_onError?: unstable_ClientOnErrorFunction,
onError?: unstable_ClientOnErrorFunction,
future?: DataRouter["future"],
): React.ReactElement | null {
invariant(
Expand Down Expand Up @@ -908,7 +908,7 @@ export function useRoutesImpl(
),
parentMatches,
dataRouterState,
unstable_onError,
onError,
future,
);

Expand Down Expand Up @@ -1103,7 +1103,7 @@ export function _renderMatches(
matches: RouteMatch[] | null,
parentMatches: RouteMatch[] = [],
dataRouterState: DataRouter["state"] | null = null,
unstable_onError: unstable_ClientOnErrorFunction | null = null,
onErrorHandler: unstable_ClientOnErrorFunction | null = null,
future: DataRouter["future"] | null = null,
): React.ReactElement | null {
if (matches == null) {
Expand Down Expand Up @@ -1187,9 +1187,9 @@ export function _renderMatches(
}

let onError =
dataRouterState && unstable_onError
dataRouterState && onErrorHandler
? (error: unknown, errorInfo?: React.ErrorInfo) => {
unstable_onError(error, {
onErrorHandler(error, {
location: dataRouterState.location,
params: dataRouterState.matches?.[0]?.params ?? {},
errorInfo,
Expand Down