Skip to content

Instantly share code, notes, and snippets.

@vabruzzo
Last active July 12, 2018 17:08
Show Gist options
  • Select an option

  • Save vabruzzo/449d0cc1acd8f4575cb2457020bff493 to your computer and use it in GitHub Desktop.

Select an option

Save vabruzzo/449d0cc1acd8f4575cb2457020bff493 to your computer and use it in GitHub Desktop.
React safeRender HoC

React safeRender higher-order component

Common errors

attempting to access properties on null or undefined values causes internal server errors, breaks entire site

Solutions

  1. defensive programming

    • idioms like if (!object) return null, object && object.value1 && object.value1.value2, and lodash's get(object, 'value1.value2')
    • consideration: no errors are thrown, need to rely on humans in some cases to notice something wrong with or missing on the front-end
  2. falcon and content editing

    • ensure data validity and correctness in falcon
    • consideration: this is good approach, but it's a longer term project and guranteeing we catch all cases is difficult, to say the least

safe rendering!

  • the safeRender higher-order component uses the new React componentDidCatch lifecycle method to gracefully handle errors and to prevent the entire site from crashing
  • it still throws the actual error, so we can still detect errors in development and in sentry

considerations:

  1. what granularity should it be utilized? so far, it's seemed most appropriate for smaller components as opposed to at the route or page level
  2. right now safeRender won't render anything if there's an error, it might be better to render a "something's wrong here" component in all environments except production so we can catch issues even quicker
import React from 'react';

const safeRender = WrappedComponent =>
  class SafeRender extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        hasError: false,
      };
    }

    componentDidCatch(error, info) {
      this.setState({ hasError: true });
      console.group(`safeRender: exception caught in component ${WrappedComponent.name}`);
      console.warn(
        'The following error and component stack were caught by safeRender, this likely means there is missing or malformed data:',
      );
      console.error(error, info.componentStack);
      console.groupEnd();
    }

    render() {
      return this.state.hasError ? null : <WrappedComponent {...this.props} />;
    }
  };

export default safeRender;

using safeRender:

export default createFragmentContainer(safeRender(FeaturedMember), {
  node: graphql`
    fragment FeaturedMember_node on ArticleFP2 {
      ...NodeLink_node
      newLeadArt {
        leadImage {
          ...ImageWiper_leadImage
        }
      }
      originalLeadArt {
        leadImage {
          ...MultiSourceImage_image
          ... on Image {
            large0_5x: imageSourceUrl(width: 600, height: 600, pixelRatio: 0.5) {
              ...MultiSourceImage_sources
            }
          }
        }
      }
    }
  `,
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment