-
Notifications
You must be signed in to change notification settings - Fork 47k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow the user to opt out of seeing "The above error..." addendum #13384
Conversation
It's unnecessary here. It was here because this method called console.error(). But we now rethrow with a clean stack, and that's worth doing regardless of whether the logging is silenced.
ReactDOM: size: -0.0%, gzip: 0.0% Details of bundled changes.Comparing: 47e217a...2b22524 react-dom
react-art
react-test-renderer
react-reconciler
react-native-renderer
Generated by 🚫 dangerJS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you consider using an expando instead of a Set/WeakSet?
packages/shared/ReactErrorUtils.js
Outdated
typeof WeakSet === 'function' ? new WeakSet() : null; | ||
|
||
(ReactErrorUtils: any).markErrorAsSuppressedInDEV = function(error: any) { | ||
const suppressedErrors = (ReactErrorUtils: any)._suppressedErrorsInDEV; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will an expando no longer work? error._suppressLogging = true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we use an expando, then I think you can get rid of the extra ReactErrorUtils methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could throw a frozen object though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it's uncommon enough that I can try/catch around it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah seems fine
7899db0
to
c433932
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool!
This partially addresses #11098.
Currently, when we have an error in development, we later print an error addendum:
This creates a lot of noise in tests for error boundaries, i.e. when you expect that an error would be thrown and handled. The original error also gets reported by jsdom even if it's wrapped in a catch block. Just like in the browser. So you end up with two
console.error
warnings you have to mock or ignore.However, both browsers and jsdom already provide a way to suppress the error message:
In this case neither the browser nor jsdom print the original error. But we still print our addendum which is now taken out of context (it says "The above error occurred" — but there's no above error!)
In this PR, I'm making React respect
e.preventDefault()
in thewindow
handler of theerror
event in a browser environment in some circumstances.In particular:
event.preventDefault()
for that error in a customerror
event handler, thenWe still show it if the error was fatal (either because you didn't have a boundary or because the boundary failed). This is to reduce the risk of accidental silencing of errors with a rogue
error
handler.What about the cases like #11098 (comment) where you don't have a boundary but still want to avoid warning noise? The solution I'd recommend is to create a boundary, and test that it caught the error.
So how would you use this in tests?
Consider this
App
component.Previously you couldn't test that error boundary worked without mocking
console.error
. Now you can:This already wouldn't print the browser error, but with this change, it doesn't print our addendum either.
There is one final detail. If the error was fatal, I think we should still show it. The problem is that if the user silenced it with
preventDefault()
, our addendum is a bit useless because it doesn't contain the error itself — only the stack. So I changed this codepath to also emit theerror
object if it has been silenced via a separateconsole.error()
call prior to the addendum log. This mimics what the browser does normally, and I think is a reasonable precaution against badly behavederror
handlers. If you want to avoid this, add error boundaries and you'll be good.I added some DOM fixtures to verify the new behavior.
These changes have no effect on production or non-DOM code paths. This means that people using test renderer or running ReactDOM in production mode would still have to mock
console.error
in tests. But I think it's reasonable to solve the most common case — and forconsole.error
, it's less of a hassle to mock when there's only one of them, and it matches the error exactly (as opposed to jsdom / React emitting two different error logs).