davnicwil / react-frontload

Async data loading for React components, on client & server
451 stars 21 forks source link

handling error during server render #61

Open matthewlein opened 2 years ago

matthewlein commented 2 years ago

Can you clarify how server rendered errors are handled? I'm trying to raise an error in the frontload to be handled in the node server render block, but I can't seem to catch the error anywhere. It seems like its being swallowed somewhere, but I can't figure it out.

If an error does throw on server render, it is not caught and populated in frontloadMetadata.error, instead it is just thrown and the entire render will fail. Such errors should be caught at the top level in your server render endpoint, and dealt with appropriately for your application.

This sounds like exactly what I want, but from what I can see, frontloadMetadata.error is populated, and the error is being caught.

frontload definitely sees the error

[react-frontload] Error on frontload [WaterbodyDetailLoader:lake-dorr], render pass 0

 RecordNotFoundError: Request failed with status code 404
 [stack trace]
 {
  name: 'RecordNotFoundError',
  url: 'waterbodies/lake-dorr'
} 

The component appears to keep rendering though, I see frontloadMeta.error true logged in the server console from the component:

export const WaterbodyDetailLoader = (props: OwnProps) => {
  const { urlSlug } = props;
  const dispatch = useDispatch();
  const { frontloadMeta } = useFrontload(
    `WaterbodyDetailLoader:${urlSlug}`,
    async () => {
      try {
        const waterbodyRes = await apiV1.waterbodyFetch(urlSlug);
      } catch (e) {
        if (e.response.status === 404) {
          throw new RecordNotFoundError(e.message, e.response.config.url);
        }
        throw e;
      }

      console.log("WaterbodyDetailLoader complete");

      return true;
    }
  );

  console.log("frontloadMeta.error", frontloadMeta.error);

  return <WaterbodyDetailContainer urlSlug={urlSlug} />;
};

Nothing is logged at the catch block of the server rendering block

try {
    const { rendered, data } = await frontloadServerRender({
      frontloadState,
      render: () =>
        renderToString(
          extractor.collectChunks(
            React.createElement(AppServer, {
              location: req.url,
              store,
              routerContext,
              frontloadState,
            })
          )
        ),
    });

    res.send(
      DOCTYPE +
        '\n' +
        renderToString(
          React.createElement(Html, {
            content: rendered,
            store,
            xForwardedFor,
            frontloadData: data,
          })
        )
    );
  } catch (err) {
    console.log('frontload error?', err);
  }

What would you expect to happen with errors in this setup?

Do you happen to have a simple demo site that can be used verify how errors work? I may make one, but if you have one already it would be helpful to rule out app code.

matthewlein commented 2 years ago

Following up on this, errors raised during component rendering are caught at the frontloadServerRender catch block level, as you would expect, but errors that are raised in the async useFrontload function appear to not be.