问题
Is it possible to skip creating a page during build if that page ends up throwing during render?
This page is created programmatically via gatsby-node createPage and it's possible that the data from a page query (from our CMS) is bad causing it to throw.
I don't want stop the build due to one bad page so ideally the page wouldn't be created or a fallback page would be put into its place (and an error event would be logged to Sentry or similar).
Any ideas on how to achieve this?
Edit: I didn't clarify my question enough so I wanted to add some context about what problem I'm trying to solve and why.
The error I'm trying to catch occurs during the render of a page during build time. This error occurs because the component I'm trying to render assumed something about the data that isn't true (but should be true).
For example, let's say I'm creating many pages for all the products on my site. The component expects that every product has imagesSizes
and calls imagesSizes.split(',')
during render. Because imagesSizes
is null
from the page query, the whole component throws an error and breaks the build.
Like @EliteRaceElephant has suggested, I have tried using React Error Boundaries and unfortunately, they don't work for SSR (which is used by Gatsby during build time). So even if I wrap my component in an error boundary, it still ends up breaking the build.
One a last note, the example I gave above is only one of the situations I run into where the data is bad and breaks the build.
What I'm trying to achieve is a simple fallback page for when any arbitrary error occurs during render during the build. My ideal solution would even allow me to throw
errors on purpose when certain assumptions I make about the data aren't true (because I'd rather send the user an error page instead of showing them a page with bad data).
Historically, when I've done SSR outside of Gatsby, I would simply wrap the whole call to ReactDOMServer.renderToString
in a try
catch
block and just return my fallback page in the catch
block.
What is the Gatsby equivalent of that?
回答1:
You can gracefully handle errors because the graphQL query is returned as a promise. Handle the raised error if the promise fails to resolve and keep building your page.
From the Gatsby node API documentation:
const path = require(`path`)
exports.createPages = ({ graphql, actions }) => {
const { createPage } = actions
const blogPostTemplate = path.resolve(`src/templates/blog-post.js`)
return graphql(`
query loadPagesQuery ($limit: Int!) {
allMarkdownRemark(limit: $limit) {
edges {
node {
frontmatter {
slug
}
}
}
}
}
`, { limit: 1000 }).then(result => {
if (result.errors) {
throw result.errors
// ##### Handle your ERROR here #######
// send to sentry, try another query, etc
// or pass an empty result object so no pages are created but the build continues
}
result.data.allMarkdownRemark.edges.forEach(edge => {
createPage({
path: `${edge.node.frontmatter.slug}`,
component: blogPostTemplate,
context: {},
})
})
})
}
EDIT #1
Your error occurs in the page template. You can handle errors in your components the React way, with error boundaries. Wrap an error boundary around your component and handle whatever goes wrong in there. The error boundary can kick off the building of an error page as well. You can handle whatever your page query returns in your PageTemplate component as well.
<PageTemplate>
<ErrorBoundary>
<YourContent />
</ErrorBoundary>
</Page Template>
EDIT #2
I understand the problem now and can offer some suggesstions. I don't think there is an easy solution since it touches the inner workings of both React and Gatsby:
try catch the non React parts of your template. Use wrapper functions that try catch errors.
I assume you have something like this inside your JSX code:
<PageTemplate>
{ imagesSizes.split(',') // do whatever and break the build }
</PageTemplate>
Instead, run all your code breaking variables through functions with try catch first. If any function catch, then you render your error page. You probably need a class base component for this. Place the functions before you call render()
let pageThrows = false;
const imageSizesSplitCheck = (images) => {
try {
imagesSizes.split(',') // throw error
} catch {
pageThrows = true; // outside of the JSX flow you can still catch errors while serverside renddering
}
}
// more try catch functions
if (pageThrows) {
// render error page
} else {
// render default page
}
It is good coding practice to handle edge cases of your data so that no exceptions are thrown. I think it is even mentioned in the Clean Code book. Otherwise you miseuse exceptions for the normal program flow. Exception should remain excpetions.
来源:https://stackoverflow.com/questions/59900772/is-it-possible-to-skip-creating-a-page-during-build-if-that-page-ends-up-throwin