-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
fix(gatsby): refresh browser when receiving update and runtime errored #27467
Conversation
3957bea
to
907b180
Compare
|
||
describe(`testing error overlay and ability to automatically recover from webpack compile errors`, () => { | ||
it(`displays content initially (no errors yet)`, () => { | ||
cy.visit(`/error-handling/compile-error/`).waitForRouteChange() |
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.
Important part in those tests that we have just single initial cy.visit
and we don't interact with page in any way (don't manually refresh, don't click anything, we only do changes to either source code or content and assert that fixing the problem cause runtime to work again automatically - even if we do refresh browser ourselves, user doesn't have to).
let registeredReloadListeners = false | ||
function onError() { | ||
if (registeredReloadListeners) { | ||
return | ||
} | ||
|
||
// Inspired by `react-dev-utils` HMR client: | ||
// If there was unhandled error, reload browser | ||
// on next HMR update | ||
module.hot.addStatusHandler(status => { | ||
if (status === `apply` || status === `idle`) { | ||
window.location.reload() | ||
} | ||
}) | ||
|
||
// Additionally in Gatsby case query result updates can cause | ||
// runtime error and also fix them, so reload on data updates | ||
// as well | ||
___emitter.on(`pageQueryResult`, () => { | ||
window.location.reload() | ||
}) | ||
___emitter.on(`staticQueryResult`, () => { | ||
window.location.reload() | ||
}) | ||
|
||
registeredReloadListeners = true | ||
} | ||
ReactErrorOverlay.startReportingRuntimeErrors({ | ||
onError: () => {}, | ||
onError, | ||
filename: `/commons.js`, | ||
}) | ||
|
||
// ReactErrorOverlay `onError` handler is triggered pretty late | ||
// so we attach same error/unhandledrejection as ReactErrorOverlay | ||
// to be able to detect runtime error and setup listeners faster | ||
window.addEventListener(`error`, onError) | ||
window.addEventListener(`unhandledrejection`, onError) | ||
|
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.
This is actual change (lot of test boilerplate in this PR, so I do want to highlight code change, so it's not lost)
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.
Looks good to me, thanks! 👍
This change seems to break existing code. With Gatsby 2.24.80 our Dev-Site is constantly reloading, while it works fine with 2.24.79. We have currently some unfixed ESLint-problems (no errors, just warnings) in the browser console, which should not trigger a reload. But maybe the problem arises from this line: 32:6 warning React Hook useEffect has a missing dependency: '...'. Either include it or remove the dependency array react-hooks/exhaustive-deps |
Hi @jablonski ! Thanks for reporting. Can you please open a new issue with a reproduction? |
I've tracked down the problem... we are including an external consent service script into our Gatsby site which works on 127.0.0.1, but throws an error on localhost. This error triggers the constant reload. Maybe this new behaviour should be configurable? I can image lots of users who will encounter Javascript errors related to third party scripts which cannot be controlled under all circumstances (think of different environments / states of the application etc.pp.). |
How does this 3rd party script get's added? Is it part of react component tree or completely separate? I could possibly change from all runtime errors to using React error boundaries to catch only react render runtime errors and not react to 3rd party scripts (as long as they are not party of react tree) |
Actually this script is loaded via Helmet within our generic page template:
|
Do you manually call functions from that script or it automatically runs what it needs to? |
The problem arises by just loading the script... We have some onClick-Handlers which trigger functions from this script later on:
|
I did reproduce the problem with just adding external script (via helmet) that just throws. From my research: What I can try doing here is to use react error boundary instead of catching all runtime errors - then errors from 3rd party scripts wouldn't trigger . I've seen some weird behaviour with |
Description
This adds crude recovery mechanism: once runtime error was found attach listeners to refresh the browser on either webpack update or data update.
Recently we fixed queueing of query running when editing query text ( #27408 ). However in runtime when users update query and component at the same time, runtime can get in error state because webpack and data updates are not applied at the same time (meaning that we can have either older component or older content for some period of time that can lead to runtime errors). This change allow to recover from situation like that.
--edit:
Please note that the change here is not just for case described above and it's rather generic. Above we ideally fix at some point by synchronising applying updates from webpack HMR and query results from websocket to avoid even getting in runtime errors scenarios, but this does cover more cases than just that ... and with some quirks it does "fix" that problem too (at least users don't have to manually refresh browser tabs)
This also updates
react-hot-loader
which does change the overlay look (and looks like overlay used by fast-refresh, because it actually is) - this might not be needed for this PR and possibly I can revert that part (I started with bumping overlay thinking I would be able to make use ofdismissRuntimeErrors
that was exposed in newer version of overlay, but ended up not using it):---EDIT:
Above screenshots are from "night mode", based on
prefers-color-scheme: dark
media feature (thatreact-error-overlay
now supports). For users with preference set tolight
, it will keep looking as it used to.I added few basic tests for error overlay interactions: (make sure we start with working site -> break something intentionally -> assert error overlay -> fix the problem -> assert that browser automatically recovered)