Skip to content

Instantly share code, notes, and snippets.

@ladifire
Created January 3, 2022 03:58
Show Gist options
  • Save ladifire/985f498ff0f4e269bf1e8586d444f715 to your computer and use it in GitHub Desktop.
Save ladifire/985f498ff0f4e269bf1e8586d444f715 to your computer and use it in GitHub Desktop.
Demonstrate how to use Error boundary component in Reactjs
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
dayjs.extend(customParseFormat); // need this because we receive non-standard date strings from some apis
export const formatDate = (formatString, date = new Date()) => dayjs(date).format(formatString);
export const parseDate = (dateToParse, formatString) => dayjs(dateToParse, formatString).toDate();
import React from 'react';
import { withErrorBoundary } from 'components/common/MartyErrorBoundary';
export const ExampleComponent = () => {
return (
<div>
Component
</div>
);
};
export default withErrorBoundary('ExampleComponent', ExampleComponent);
/* eslint-disable no-console */
/* global __DEVELOPMENT__ */
import ExecutionEnvironment from 'exenv';
import debounce from 'lodash.debounce';
import { formatDate } from './dateUtils';
if (ExecutionEnvironment.canUseDOM
&& (typeof window.console === 'undefined' || window.console.log === undefined || window.console.debug === undefined || window.console.error === undefined)) {
// polyfill
window.console = window.console || {};
window.console.log = () => {};
window.console.debug = () => {};
window.console.error = () => {};
}
function logToErrCgi(name, trace) {
if (window && typeof window.onerror === 'function') {
window.onerror(name, window.location.href, trace);
}
}
function modifyLogCall(logFunc, clientOrServer, args) {
console[logFunc].apply(console, [clientOrServer].concat(Array.prototype.slice.call(args)));
}
function getClientLogger(logFunc) {
return function() {
modifyLogCall(logFunc, 'Marty Client:', arguments);
};
}
function getServerLogger(logFunc) {
const env = process.env.MARTY_EB_ENV || 'unset';
if (process.env.NODE_ENV === 'test') {
return () => null;
}
// needs to be function rather than fat arrow or else `arguments` get lost
return function() {
const timestamp = formatDate('DD/MMM/YYYY:HH:mm:ss.ms ZZ');
const loggingPrefix = `[${timestamp}] martyenv=${env}`;
modifyLogCall(logFunc, loggingPrefix, arguments);
};
}
export function devLogger(message) {
if (__DEVELOPMENT__) {
logger(message);
}
}
export class DevLoggerGroupDebounced {
constructor({ groupName, debounceTime }) {
this.groupName = groupName;
this.debounceTime = debounceTime;
this.itemsToLog = [];
}
logGroup = debounce(() => {
console.groupCollapsed(this.groupName);
this.itemsToLog.forEach(item => console.log(item));
console.groupEnd();
this.itemsToLog = [];
}, this.debounceTime);
addLog = item => {
if (__DEVELOPMENT__ && typeof console.groupCollapsed === 'function') {
this.itemsToLog.push(item);
this.logGroup();
}
};
}
const logger = ExecutionEnvironment.canUseDOM ? getClientLogger('log') : getServerLogger('log');
export const logError = ExecutionEnvironment.canUseDOM ? getClientLogger('error') : getServerLogger('error');
export const logDebug = ExecutionEnvironment.canUseDOM ? getClientLogger('debug') : getServerLogger('log');
export const logToServer = logError;
export const logErrorAndLogToServer = error => {
logError(error);
logToServer(error);
};
export default logger;
import React, { forwardRef, useCallback } from 'react';
import ErrorBoundary from 'react-error-boundary';
import { logErrorAndLogToServer } from './logger';
interface Props {
boundaryTag: string;
children: React.ReactNode;
}
const WithErrorBoundary = ({ boundaryTag, children }: Props) => {
// useCallback w/ react-hooks so we don't pass a brand new function every time this re-renders
const FallbackComponent = useCallback(() => <div style={{ display: 'none' }} data-component-boundary-error={`Error while rendering the component ${boundaryTag ? `([${boundaryTag}])` : ''}`}/>, [boundaryTag]);
const onError = useCallback(() => (error: Error) => {
const errorMessage = `An error has occurred while rendering component contained with WithErrorBoundary: ${error.toString()}`;
logErrorAndLogToServer(`${boundaryTag ? `[${ boundaryTag}]: ` : ''}${errorMessage}`);
}, [boundaryTag]);
return <ErrorBoundary FallbackComponent={FallbackComponent} onError={onError}>{children}</ErrorBoundary>;
};
export const withErrorBoundary = <Props extends object>(boundaryTag: string, Component: React.ComponentType<Props>) => {
const WrappedComponent = forwardRef<React.ReactNode, Props>((props, ref) => <WithErrorBoundary boundaryTag={boundaryTag}><Component ref={ref} {...props}/></WithErrorBoundary>);
WrappedComponent.displayName = `${getDisplayName(Component)}ErrorBoundary`;
return WrappedComponent;
};
// helper for HOC
function getDisplayName(WrappedComponent: React.ComponentType<any>) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
export default WithErrorBoundary;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment