import React from 'react'; import PropTypes from 'prop-types'; import {connect} from 'react-redux'; import bowser from 'bowser'; import BrowserModalComponent from '../components/browser-modal/browser-modal.jsx'; import CrashMessageComponent from '../components/crash-message/crash-message.jsx'; import log from '../lib/log.js'; import supportedBrowser from '../lib/supported-browser'; import analytics from '../lib/analytics'; class ErrorBoundary extends React.Component { constructor (props) { super(props); this.state = { hasError: false }; } componentDidCatch (error, info) { // Error object may be undefined (IE?) error = error || { stack: 'Unknown stack', message: 'Unknown error' }; // Display fallback UI this.setState({hasError: true}); // Log errors to analytics, separating supported browsers from unsupported. if (supportedBrowser()) { analytics.event({ category: 'error', action: this.props.action, label: error.message }); } else { analytics.event({ category: 'Unsupported Browser Error', action: `(Unsupported Browser) ${this.props.action}`, label: `${bowser.name} ${error.message}` }); } // Log error locally for debugging as well. log.error(`Unhandled Error: ${error.stack}\nComponent stack: ${info.componentStack}`); } handleBack () { window.history.back(); } handleReload () { window.location.replace(window.location.origin + window.location.pathname); } render () { if (this.state.hasError) { if (supportedBrowser()) { return <CrashMessageComponent onReload={this.handleReload} />; } return (<BrowserModalComponent isRtl={this.props.isRtl} onBack={this.handleBack} />); } return this.props.children; } } ErrorBoundary.propTypes = { action: PropTypes.string.isRequired, // Used for defining tracking action children: PropTypes.node, isRtl: PropTypes.bool }; const mapStateToProps = state => ({ isRtl: state.locales.isRtl }); // no-op function to prevent dispatch prop being passed to component const mapDispatchToProps = () => ({}); export default connect(mapStateToProps, mapDispatchToProps)(ErrorBoundary);