Replies: 10 comments 17 replies
-
React handles recoverable errors in onRecoverableError. Either a custom function is passed to hydrateRoot; otherwise React's default implementation is used, which uses the browser's reportError function. Errors that are recoverable in isolation are always reported to the user in a modal-style error dialogue, and are not handled quietly. NextJs uses only two arguments (Dom element, React element) when calling hydrateRoot. The third possible argument to specify hydrate options along with the onRecoverableError field, in which the error response of recoverable errors can be adjusted, is not used at all. In principle, the problem can be addressed on the NextJs or React side, but an adjustment is necessary on the respective side. Option 1: NextJs - Extension of the NextJs configuration Con: Valid only for NextJs, other libraries that hide the call to hydrateRoot from the user and do not allow hydrate options are still affected by the issue. Option 2: Adaptation of React's default implementation to onRecoverableError Errors related to suspense are called recoverable. These are resolved through React internal workflows by reverting to a client-based rendering within a Suspense Boundary. As these errors are handled, jumping back to a client-based rendering does not constitute a critical error. A hint in the console about the cause that led to the switch to a client-based rendering should be sufficient.
https://developer.mozilla.org/en-US/docs/Web/API/reportError As pointed out in the comment, an uncaught error is thrown even if it was classified as recoverable within React. React internal processes have responded to such errors, for example by server-side and client-side content not matching. The error caused by reportError must in turn be caught by a global error handler. This seems to be a bit excessive.
In React based libraries that do not allow or support configuration of hydrate options, the problem must be described as recurring. Especially in background that React notices recoverable errors and actively counteracts them, conversely should resort to a less aggressive behaviour to bring this to the developer's attention. ReportError is a very young API - Supported from October 2021 in Edge, Firefox and Chrome, not before. https://caniuse.com/?search=reportError LogRecoverableError was originally introduced in ReactDOMHostConfig. The following refactoring has changed the name and position. logRecoverableError => defaultOnRecoverableError I will open an issue in NextJs and React respectively. Thx! |
Beta Was this translation helpful? Give feedback.
-
This should really be converted back to an issue. Theres plenty of users confused by minified errors which can sometimes only be reproduced in production. Supporting |
Beta Was this translation helpful? Give feedback.
-
Hate to be the person to drop by and add a plus one to a discussion but we'd find this feature really valuable to help us debug these errors! We'd be happy to also contribute this feature if given some guidance on the best place to support this (my limited understanding is that next.config.js isn't loaded on the client, so we wouldn't be able to leverage that to define a function for this handler, I guess it could be defined in something like _app, or layout.tsx in the new infra [but that gets tricky if it's a server component 🤔 ]) |
Beta Was this translation helpful? Give feedback.
-
Would be great to see this get exposed. We've been chatting about it here facebook/react#26224, and it seems Astro, Remix, Gatsby all already expose this functionality so Next.js just has to catch up! if you want a super hacky approach, you can try and monkey patch // Run this code before react components get mounted.
// If you use Sentry you can put this in your sentry.client.config.js
import * as client from "react-dom/client";
const oldClientHydrateRoot = client.hydrateRoot;
client.hydrateRoot = new Proxy(oldClientHydrateRoot, {
apply: (wrappingTarget, thisArg, args) => {
const oldOnRecoverableError = args[2].onRecoverableError;
args[2].onRecoverableError = new Proxy(oldOnRecoverableError, {
apply: (inner_wrappingTarget, inner_thisArg, inner_args) => {
const error = inner_args[0];
const errorInfo = inner_args[1];
// log out error + `componentStack` in production
console.log(error, errorInfo.componentStack)
// if y'all use Sentry (https://sentry.io/for/nextjs/)
// Sentry.captureException(error, { contexts: { react: { componentStack } } });
});
return wrappingTarget.apply(thisArg, args);
},
}); ^ We use the above to get the component stack logged in production! |
Beta Was this translation helpful? Give feedback.
-
Relates to #47542. |
Beta Was this translation helpful? Give feedback.
-
Principal Engineer on the Web team at @target here - we would find this extremely useful as well! |
Beta Was this translation helpful? Give feedback.
-
Proposing #50943 as a potential partial solution here, at least for @AbhiPrasad's use case above, which my teams also have. |
Beta Was this translation helpful? Give feedback.
-
I think it makes sense to have it exposed completely as well. There is a pattern described in React docs that enables real client-only components without use of Other frameworks like Remix do not hide the |
Beta Was this translation helpful? Give feedback.
-
Far from being an optimal solution, but I built a modal that shows a diff of client vs server rendered DOM when hydration errors occur: https://github.com/BuilderIO/hydration-overlay/ Somewhat of a workaround until this discussion bears fruit, or Hydration error handling improves within React itself. |
Beta Was this translation helpful? Give feedback.
-
@AbhiPrasad I tried your hack and although I log some info I still unable to retrieve what caused the hydration error. In Next 14 a diff was introduced that shows a diff with exactly what the server and the client rendered. It is also log to the console: But the info available How do I get this diff? What else are you at doing to be able to display a diff in Sentry replay? |
Beta Was this translation helpful? Give feedback.
-
Describe the feature you'd like to request
With the upgrade to React 18, we are getting a lot of hydration errors. These can be hard to debug on production because the source maps are minified.
I'd like to see the ability to pass in
onRecoverableError
tohydrateRoot
which will allow better logging of errors to help us track down hydration issues. (This would also let us ignore hydration errors if we decide to, or not send them to Sentry.)Describe the solution you'd like
You can define an
onRecoverableError
innext.config.js
which will be called onhydrateRoot
.Describe alternatives you've considered
I've tried tracking down hydration errors and I've found some of them, but not all of them.
Beta Was this translation helpful? Give feedback.
All reactions