Skip to content
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

feat: use error boundary to capture useEffect errors #539

Merged
merged 1 commit into from Jan 13, 2021

Conversation

mpeyper
Copy link
Member

@mpeyper mpeyper commented Jan 13, 2021

What:

Replace try/catch error handling with a an error boundary.

Why:

Fixes #308

The error boundary will catch errors thrown in useEffect calls as well as in the render path.

How:

The challenge here and why we haven't done it sooner is the excessive amount of logging produced from React when an error boundary catches and error. I've tried many official and unofficial approaches over the years but none have worked consistently as we changed renderers and new versions of React were released. For more information, see these issues:

In this proposal, I used a package called filter-console to prevent errors matching a specific patterns from being logged. This is potentially brittle if new versions of React change the log format, but we can add more patterns or change the existing ones if they ever do πŸ€·β€β™‚οΈ

I also opted to use react-error-boundary as it provided some nice features for resetting the error state when rerendering. The way I access the reset functionality is a bit clunky, so I'm open to suggestions on how to clean that up.

Checklist:

  • Tests
  • Ready to be merged

We had a bunch of tests marked as .skip for testing the effect errors that I have unmarked and they all work now, which is a good sign.

The other tests that needed updating was the result history tests . The hook used in those tests actually had a side effect from rendering that the try/catch was hiding, but the error boundary showed the results appearing out of order because the error handling triggered an additional render that cause the next result to jump ahead in the array. It was painful to debug, but the new tests worked against the old implementation and as well as the new one.

@codecov
Copy link

codecov bot commented Jan 13, 2021

Codecov Report

Merging #539 (6ffcc84) into beta (008077c) will not change coverage.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff            @@
##              beta      #539   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           14        14           
  Lines          202       210    +8     
  Branches        27        23    -4     
=========================================
+ Hits           202       210    +8     
Impacted Files Coverage Ξ”
src/helpers/createTestHarness.tsx 100.00% <100.00%> (ΓΈ)
src/helpers/promises.ts 100.00% <100.00%> (ΓΈ)
src/server/pure.ts 100.00% <100.00%> (ΓΈ)

Continue to review full report at Codecov.

Legend - Click here to learn more
Ξ” = absolute <relative> (impact), ΓΈ = not affected, ? = missing data
Powered by Codecov. Last update 008077c...6ffcc84. Read the comment docs.

export async function callAfter(callback: () => void, ms: number) {
async function callAfter(callback: () => void, ms: number) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved the the export at the end of the file

Comment on lines +23 to +28
try {
const serverOutput = ReactDOMServer.renderToString(testHarness(props))
container.innerHTML = serverOutput
} catch (e: unknown) {
rendererProps.setError(e as Error)
}
Copy link
Member Author

@mpeyper mpeyper Jan 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out error boundaries throw synchronously when server rendering. This is the only renderer that has any special treatment for error handling and only in the initial render function. The error boundary behaves normally once it has been hydrated.

Comment on lines +34 to +38
const TestComponent = ({ hookProps }: { hookProps?: TProps }) => {
// coerce undefined into TProps, so it maintains the previous behaviour
setValue(callback(hookProps as TProps))
return null
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I brought this into createTestHarness because:

  1. ErrorFallback needed to be inside and I like consistency
  2. It simplified the props of TestComponent as the rendererProps are already available in the outer scope

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I originally moved it out before we moved out testHarness so It makes sense to put it back in since TestComponent isn't required on it's own.

Copy link
Member

@joshuaellis joshuaellis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, be good to monitor over the lifecycle of react too, think we might need to watch carefully.

@joshuaellis joshuaellis merged commit b81fd04 into beta Jan 13, 2021
@joshuaellis joshuaellis deleted the feat/error-boundary branch January 13, 2021 18:43
@github-actions
Copy link

πŸŽ‰ This PR is included in version 5.0.0-beta.12 πŸŽ‰

The release is available on:

Your semantic-release bot πŸ“¦πŸš€

@github-actions
Copy link

πŸŽ‰ This PR is included in version 5.0.0 πŸŽ‰

The release is available on:

Your semantic-release bot πŸ“¦πŸš€

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants