Created
January 3, 2022 03:58
-
-
Save ladifire/985f498ff0f4e269bf1e8586d444f715 to your computer and use it in GitHub Desktop.
Demonstrate how to use Error boundary component in Reactjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import { withErrorBoundary } from 'components/common/MartyErrorBoundary'; | |
export const ExampleComponent = () => { | |
return ( | |
<div> | |
Component | |
</div> | |
); | |
}; | |
export default withErrorBoundary('ExampleComponent', ExampleComponent); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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