Is it possible to skip creating a page during build if that page ends up throwing during render?

梦想的初衷 提交于 2020-02-25 04:37:48

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!