├── .babelrc
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── example
├── index.html
└── src
│ ├── FilterableProductTable.jsx
│ └── index.jsx
├── package-lock.json
├── package.json
├── src
├── Fallback.js
├── Modal.js
├── WithErrorHandler.js
└── index.js
├── webpack.config.js
└── webpack.dev.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "commonjs": {
4 | "presets": [
5 | "es2015",
6 | "react",
7 | "stage-3"
8 | ],
9 | "plugins": [
10 | "transform-decorators-legacy"
11 | ]
12 | },
13 | "es": {
14 | "presets": [
15 | "es2015-rollup",
16 | "react",
17 | "stage-3"
18 | ],
19 | "plugins": [
20 | "transform-runtime",
21 | "transform-decorators-legacy"
22 | ]
23 | },
24 | "production": {
25 | "comments":false,
26 | "presets": [
27 | "es2015",
28 | "react",
29 | "stage-3"
30 | ],
31 | "plugins": [
32 | "transform-runtime",
33 | "transform-decorators-legacy"
34 | ]
35 | }
36 | },
37 | "presets": [
38 | "es2015",
39 | "react",
40 | "stage-3"
41 | ],
42 | "plugins": [
43 | "transform-decorators-legacy"
44 | ]
45 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 | build
60 | dist
61 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | .DS_STORE
40 | *.log
41 | example
42 | examples
43 | build
44 | src
45 | LICENSE
46 | webpack.*.js
47 | *.js.map
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Chen HaiYong
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-error-boundaries
2 |
3 | [](http://npm.im/react-popconfirm)
4 | [](http://npm-stat.com/charts.html?package=react-popconfirm&from=2017-04-07)
5 | [](http://opensource.org/licenses/MIT)
6 |
7 | A reusable React error boundaries component. Based on React 16.2.0.
8 |
9 | > Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.
10 |
11 | Effect picture:
12 |
13 | 
14 |
15 | ## Install
16 |
17 | ```s
18 | npm install react-error-boundaries
19 | ```
20 |
21 | ## Usage
22 |
23 | Import:
24 |
25 | ```js
26 | // import all
27 | import { ErrorBoundary, withErrorHandler, errorHandlerDecorator, FallbackView } from 'react-error-boundaries'
28 | // import default ErrorBoundary
29 | import ErrorBoundary from 'react-error-boundaries'
30 | ```
31 |
32 | Intro:
33 |
34 | - **ErrorBoundary**: React container component to handler error
35 | - **withErrorHandler**: React HOC to customize the errorCallback function and FallbackComponent
36 | - **errorHandlerDecorator**: By this, you can use error boundary as ES7 decorator
37 | - **FallbackView**: The default fallback component, show when error occur. props: `{ error: Objec, errorInfo: Object, closeErrorModal: Function }`(Unable in production, if import you will got undefined)
38 |
39 | Use as a component container:
40 |
41 | ```js
42 | // import first
43 | import React from 'react'
44 | import ReactDOM from 'react-dom'
45 | import ErrorBoundary from 'react-error-boundaries'
46 |
47 | const App = () => {
48 | return (
49 |
50 |
51 |
52 | );
53 | }
54 | ReactDOM.render(, document.getElementById('root'));
55 | ```
56 |
57 | And you can handle errors by providing an onError callback, and customize the FallbackComponent by providing a Component.
58 |
59 | > FallbackComponent will receive props:
60 | - closeErrorModal: Function, call when click close button
61 | - error: An error that has been thrown.
62 | - errorInfo: An object with componentStack key. The property has information about component stack during thrown error.
63 |
64 | ```js
65 | // import first
66 | import React from 'react'
67 | import ReactDOM from 'react-dom'
68 | import ErrorBoundary from 'react-error-boundaries'
69 |
70 | function onError(error, errorInfo, props) {
71 | // you can report Error to service here
72 | console.error('onError:', error.message);
73 | }
74 |
75 | const App = () => {
76 | return (
77 |
78 |
79 |
80 | );
81 | }
82 | ReactDOM.render(, document.getElementById('root'));
83 | ```
84 |
85 | Use as class decorator:
86 |
87 | ```js
88 | // import first
89 | import React from 'react'
90 | import { errorHandlerDecorator } from 'react-error-boundaries'
91 |
92 | // ES7 decorator, need babel plugin "transform-decorators-legacy"
93 | @errorHandlerDecorator
94 | class YourComponent extends React.Component {
95 | constructor(props) {
96 | super(props);
97 | }
98 |
99 | render() {
100 | return (
101 |
102 | contents
103 |
104 | );
105 | }
106 | }
107 | // or not use @decorator just like this:
108 | // export default errorHandlerDecorator(YourComponent)
109 |
110 | function onError(error, errorInfo, props) {
111 | // you can report Error to service here
112 | console.log('onError:', error.message);
113 | }
114 |
115 | ReactDOM.render(, document.getElementById('root'));
116 | ```
117 |
118 | You can also customize the FallbackComponent in HOC way:
119 |
120 | ```js
121 | // import first, FallbackView is default Fallback Component
122 | import { withErrorHandler, FallbackView } from 'react-error-boundaries'
123 |
124 | // customize the errorCallback
125 | function onError(error, errorInfo, props) {
126 | // you can report Error to service here
127 | console.error('onError:', error.message);
128 | }
129 |
130 | /* example 1 */
131 |
132 | const ComponentWithErrorBoundary = withErrorHandler(
133 | YourFallbackComponent, // Fallback Component to display errors, to replace default FallbackView
134 | YourComponent // Component to decorate
135 | );
136 | ReactDOM.render(, document.getElementById('root'));
137 |
138 | /* example 2 */
139 | // customize as a ES7 decorator
140 | const yourErrorHandlerDecorator = withErrorHandler(
141 | YourFallbackComponent // Fallback Component to display errors, to replace default FallbackView
142 | );
143 |
144 | @yourErrorHandlerDecorator
145 | class YourComponent extends React.component {
146 | //......
147 | }
148 | ReactDOM.render(, document.getElementById('root'));
149 | ```
150 |
151 | ## Try example
152 |
153 | Input `i` in search input and error will throw.
154 |
155 | ```shell
156 | # run example, auto open browser and enable hot loader
157 | npm install
158 | npm start
159 | ```
160 |
161 | ## How to disable it
162 |
163 | To enable it by set `process.env.NODE_ENV` or `process.env.ERROR_ENV` as `development`, so you can disable it by setting `process.env.NODE_ENV` to be `production` and not set `process.env.ERROR_ENV` as `development`.
164 |
165 | With webpack by setting like this to disable it:
166 |
167 | ```js
168 | plugins: [
169 | new webpack.DefinePlugin({
170 | "process.env": {
171 | NODE_ENV: '"production"'
172 | }
173 | })
174 | ]
175 | ```
176 |
177 | With config like this to enable it even in NODE_ENV is production:
178 |
179 | ```js
180 | plugins: [
181 | new webpack.DefinePlugin({
182 | "process.env": {
183 | NODE_ENV: '"production"',
184 | ERROR_ENV: '"development"'
185 | }
186 | })
187 | ]
188 | ```
189 |
190 | ## License
191 |
192 | MIT
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | React Error Boundary
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/src/FilterableProductTable.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { errorHandlerDecorator } from '../../dist/commonjs'
3 |
4 | class ProductCategoryRow extends React.PureComponent {
5 | render() {
6 | return ({this.props.category} |
);
7 | }
8 | }
9 |
10 | class ProductRow extends React.PureComponent {
11 | render() {
12 | var name = this.props.product.stocked ?
13 | this.props.product.name :
14 |
15 | {this.props.product.name}
16 | ;
17 | return (
18 |
19 | {name} |
20 | {this.props.product.price} |
21 |
22 | );
23 | }
24 | }
25 |
26 | class ProductTable extends React.PureComponent {
27 | render() {
28 | if (this.props.filterText === 'i') {
29 | // Simulate a JS error
30 | throw new Error('I crashed! (Test Error Boundaries when input "i")');
31 | }
32 | var rows = [];
33 | var lastCategory = null;
34 | console.log('filter:' + this.props.filterText, 'inStockOnly:' + this.props.inStockOnly)
35 | this.props.products.forEach((product) => {
36 | if (product.name.indexOf(this.props.filterText) === -1 || (!product.stocked && this.props.inStockOnly)) {
37 | return;
38 | }
39 | if (product.category !== lastCategory) {
40 | rows.push();
41 | }
42 | rows.push();
43 | lastCategory = product.category;
44 | });
45 | return (
46 |
47 |
48 |
49 | Name |
50 | Price |
51 |
52 |
53 | {rows}
54 |
55 | );
56 | }
57 | }
58 |
59 | class SearchBar extends React.PureComponent {
60 | constructor(props) {
61 | super(props);
62 | this.handleFilterTextInputChange = this.handleFilterTextInputChange.bind(this);
63 | this.handleInStockInputChange = this.handleInStockInputChange.bind(this);
64 | }
65 |
66 | handleFilterTextInputChange(e) {
67 | this.props.onFilterTextInput(e.target.value);
68 | }
69 |
70 | handleInStockInputChange(e) {
71 | this.props.onInStockInput(e.target.checked);
72 | }
73 |
74 | render() {
75 | return (
76 |
93 | );
94 | }
95 | }
96 |
97 | function onError(error, errorInfo, props) {
98 | console.log('FilterableProductTable.onError:', error, errorInfo, props);
99 | }
100 |
101 | // @errorHandlerDecorator // use ES7 decorator
102 | export default class FilterableProductTable extends React.PureComponent {
103 | constructor(props) {
104 | super(props);
105 | this.state = {
106 | filterText: '',
107 | inStockOnly: false
108 | };
109 |
110 | this.handleFilterTextInput = this.handleFilterTextInput.bind(this);
111 | this.handleInStockInput = this.handleInStockInput.bind(this);
112 | }
113 |
114 | handleFilterTextInput(filterText) {
115 | this.setState({
116 | filterText: filterText
117 | });
118 | }
119 |
120 | handleInStockInput(inStockOnly) {
121 | this.setState({
122 | inStockOnly: inStockOnly
123 | })
124 | }
125 |
126 | render() {
127 | return (
128 |
141 | );
142 | }
143 | }
144 |
145 |
146 | var PRODUCTS = [
147 | { category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football' },
148 | { category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball' },
149 | { category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball' },
150 | { category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch' },
151 | { category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5' },
152 | { category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7' }
153 | ];
154 |
155 | export { PRODUCTS };
156 | // or not use @decorator
157 | // export default errorHandlerDecorator(FilterableProductTable)
--------------------------------------------------------------------------------
/example/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 |
4 | import FilterableProductTable, { PRODUCTS } from './FilterableProductTable'
5 | import ErrorBoundary from '../../dist/commonjs'
6 |
7 | const App = () => {
8 | return (
9 |
10 |
11 |
12 | );
13 | }
14 |
15 | function onError(error, errorInfo, props) {
16 | console.warn('App.onError:', error, errorInfo, props);
17 | }
18 |
19 | ReactDOM.render(, document.getElementById('root'));
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-error-boundaries",
3 | "version": "1.1.4",
4 | "description": "React HOC for error boundaries.",
5 | "main": "dist/commonjs/index.js",
6 | "scripts": {
7 | "build": "npm run build:commonjs && npm run build:es && npm run build:umd",
8 | "build:commonjs": "npm run clean:commonjs && cross-env NODE_ENV=production cross-env BABEL_ENV=commonjs babel src --out-dir dist/commonjs",
9 | "build:es": "npm run clean:es && cross-env NODE_ENV=production cross-env BABEL_ENV=es babel src --out-dir dist/es",
10 | "build:umd": "npm run clean:umd && cross-env NODE_ENV=production npm run webpack:umd",
11 | "clean": "npm run clean:commonjs && npm run clean:es && npm run clean:umd",
12 | "clean:commonjs": "rimraf dist/commonjs",
13 | "clean:es": "rimraf dist/es",
14 | "clean:umd": "rimraf dist/umd",
15 | "webpack": "webpack --progress --colors --bail --define",
16 | "webpack:umd": "npm run webpack umd",
17 | "build:example": "webpack --config webpack.dev.config.js --progress --colors",
18 | "start": "webpack-dev-server --config webpack.dev.config.js --progress --colors --hot --inline"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/Chyrain/react-error-boundaries.git"
23 | },
24 | "keywords": [
25 | "react",
26 | "HOC",
27 | "error",
28 | "boundary"
29 | ],
30 | "author": "Chyrain. (https://chyrain.github.io/)",
31 | "bugs": {
32 | "url": "https://github.com/Chyrain/react-error-boundaries/issues"
33 | },
34 | "homepage": "https://github.com/Chyrain/react-error-boundaries#readme",
35 | "license": "MIT",
36 | "peerDevDependencies": {
37 | "react": "^16.0.0",
38 | "react-dom": "^16.0.0"
39 | },
40 | "devDependencies": {
41 | "babel-core": "^6.26.0",
42 | "babel-loader": "^7.1.2",
43 | "babel-plugin-transform-decorators-legacy": "^1.3.4",
44 | "babel-plugin-transform-runtime": "^6.23.0",
45 | "babel-preset-es2015": "^6.24.1",
46 | "babel-preset-es2015-rollup": "^3.0.0",
47 | "babel-preset-react": "^6.24.1",
48 | "babel-preset-stage-3": "^6.24.1",
49 | "cross-env": "^5.1.3",
50 | "html-webpack-plugin": "^2.30.1",
51 | "open-browser-webpack-plugin": "0.0.5",
52 | "webpack": "^3.10.0",
53 | "webpack-dev-server": "^2.9.7",
54 | "react": "^16.2.0",
55 | "react-dom": "^16.2.0"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Fallback.js:
--------------------------------------------------------------------------------
1 | // Fallback.jsx
2 | import React from 'react'
3 | import Modal from './Modal'
4 |
5 | const containerStyl = {
6 | boxSizing: 'border-box',
7 | backgroundColor: 'rgba(0,0,0,0.75)',
8 | color: '#fff',
9 | position: 'fixed',
10 | height: '100%',
11 | width: '100%',
12 | top: 0,
13 | left: 0,
14 | overflowY: 'auto',
15 | padding: '16px'
16 | };
17 | const btnStyl = {
18 | position: 'absolute',
19 | right: '16px',
20 | top: '35px',
21 | cursor: 'pointer',
22 | minWidth: '40px',
23 | minHeight: '30px',
24 | borderRadius: '5px',
25 | outlineStyle: 'none'
26 | };
27 | const preSty = {
28 | whiteSpace: 'pre-wrap',
29 | wordWrap: 'break-word',
30 | wordBreak: 'break-word',
31 | padding: '0 6px'
32 | };
33 | const detailSty = {
34 | outline: 'none',
35 | WebkitTapHighlightColor: 'transparent',
36 | WebkitHighlight: 'none'
37 | };
38 |
39 | export default function Fallback(props) {
40 | const { error, errorInfo, closeErrorModal } = props;
41 |
42 | return (
43 |
44 |
45 |
46 |
47 |
Something went wrong.
48 |
49 |
50 | ErrorMessage
51 |
52 |
{error.message}
53 |
54 |
55 | Stack
56 |
57 |
{error.stack}
58 |
59 |
60 | ComponentStack
61 |
62 |
{errorInfo.componentStack}
63 |
64 |
65 |
66 |
67 | )
68 | }
--------------------------------------------------------------------------------
/src/Modal.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 |
4 | export default class Modal extends React.PureComponent {
5 | constructor(props) {
6 | super(props);
7 | // Create a div that we'll render the modal into. Because each
8 | // Modal component has its own element, we can render multiple
9 | // modal components into the modal container.
10 | this.el = document.createElement('div');
11 | }
12 |
13 | componentDidMount() {
14 | // Append the element into the DOM on mount. We'll render
15 | // into the modal container element (see the HTML tab).
16 | document.body.appendChild(this.el);
17 | }
18 |
19 | componentWillUnmount() {
20 | // Remove the element from the DOM when we unmount
21 | document.body.removeChild(this.el);
22 | }
23 |
24 | render() {
25 | // Use a portal to render the children into the element
26 | return ReactDOM.createPortal(
27 | // Any valid React child: JSX, strings, arrays, etc.
28 | this.props.children,
29 | // A DOM element
30 | this.el
31 | );
32 | }
33 | }
--------------------------------------------------------------------------------
/src/WithErrorHandler.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import FallbackView from "./Fallback";
3 |
4 | class ErrorBoundary extends React.PureComponent {
5 | constructor() {
6 | super();
7 |
8 | this.closeErrorModal = this.closeErrorModal.bind(this);
9 | this.state = {
10 | hasError: false,
11 | error: null,
12 | errorInfo: null
13 | };
14 | }
15 |
16 | closeErrorModal() {
17 | this.setState({ hasError: false });
18 | }
19 |
20 | componentDidCatch(error, info) {
21 | // Update state if error happens
22 | this.setState({ hasError: true, error, errorInfo: info });
23 |
24 | // Report errors here
25 | const { onError, FallbackComponent, ..._props } = this.props;
26 | if (typeof onError === "function") {
27 | try {
28 | onError.call(this, error, info, _props);
29 | } catch (e) {}
30 | }
31 | }
32 |
33 | render() {
34 | const { onError, FallbackComponent, children, ..._props } = this.props;
35 | // if state contains error and in development environment we render fallback component
36 | if (this.state.hasError) {
37 | const { error, errorInfo } = this.state;
38 | return (
39 |
45 | );
46 | }
47 | return children;
48 | }
49 | }
50 | ErrorBoundary.defaultProps = {
51 | FallbackComponent: FallbackView
52 | };
53 |
54 | export { ErrorBoundary, FallbackView };
55 | export default ErrorBoundary;
56 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | let __ErrorBoundary;
4 | if (
5 | process.env.NODE_ENV === "development" ||
6 | process.env.ERROR_ENV === "development"
7 | ) {
8 | const { ErrorBoundary, FallbackView } = require("./WithErrorHandler");
9 |
10 | const withErrorHandler = curry((FallbackComponent, Component) => {
11 | const WithErrorHandler = props => {
12 | const { onError } = props;
13 | return (
14 |
15 |
16 |
17 | );
18 | };
19 | WithErrorHandler.displayName = `WithErrorHandler(${Component.displayName ||
20 | Component.name ||
21 | "Component"})`;
22 | return WithErrorHandler;
23 | });
24 | __ErrorBoundary = ErrorBoundary;
25 | exports.ErrorBoundary = ErrorBoundary;
26 | exports.FallbackView = FallbackView;
27 | exports.withErrorHandler = withErrorHandler;
28 | exports.errorHandlerDecorator = withErrorHandler(FallbackView);
29 | } else {
30 | // production or other env (not development)
31 | // NOOP ErrorBoundary
32 | class ErrorBoundary extends React.Component {
33 | componentDidCatch(error, info) {
34 | const { onError, ..._props } = this.props;
35 | if (typeof onError === "function") {
36 | try {
37 | onError.call(this, error, info, _props);
38 | } catch (e) {}
39 | }
40 | }
41 |
42 | render() {
43 | return this.props.children;
44 | }
45 | }
46 | // NOOP HOC
47 | const withErrorHandler = curry((FallbackComponent, Component) => {
48 | const WithErrorHandler = props => {
49 | const { onError } = props;
50 | return (
51 |
52 |
53 |
54 | );
55 | };
56 | return WithErrorHandler;
57 | });
58 | __ErrorBoundary = ErrorBoundary;
59 | exports.ErrorBoundary = ErrorBoundary;
60 | exports.withErrorHandler = withErrorHandler;
61 | exports.errorHandlerDecorator = withErrorHandler(void 0);
62 | }
63 |
64 | function curry(fn) {
65 | if (typeof fn !== "function") {
66 | throw Error("curry only receive function params!");
67 | }
68 | let _len = fn.length,
69 | _args = [];
70 |
71 | function _curry() {
72 | var args = [].concat(_args);
73 | if (arguments.length >= _len) {
74 | _args = [];
75 | } else if (arguments.length + _args.length > _len) {
76 | _args = [];
77 | }
78 | _args = _args.concat([].slice.call(arguments));
79 | if (_args.length === _len) {
80 | var rst = fn.apply(null, _args);
81 | _args = args;
82 | return rst;
83 | }
84 | return _curry;
85 | }
86 | _curry.toString = function() {
87 | return fn.toString();
88 | };
89 | return _curry;
90 | }
91 |
92 | export default __ErrorBoundary;
93 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | // webpack.config.js
2 | const path = require('path');
3 | const webpack = require('webpack');
4 | const argv = require('yargs').argv;
5 | const HtmlwebpackPlugin = require('html-webpack-plugin');
6 |
7 | const SRC_PATH = path.resolve(__dirname, 'src');
8 | const BUILD_PATH = path.resolve(__dirname, 'dist');
9 | const EXAMPLE_PATH = path.resolve(__dirname, 'example');
10 |
11 | const libTarget = argv.define || 'commonjs';
12 |
13 | module.exports = {
14 | // Source Maps("source-map|cheap-module-source-map|eval-source-map|cheap-module-eval-source-map")
15 | devtool: 'source-map',
16 | entry: {
17 | index: path.resolve(SRC_PATH, 'index.js')
18 | },
19 | output: {
20 | path: path.resolve(BUILD_PATH, libTarget),
21 | filename: '[name].js',
22 | libraryTarget: libTarget,
23 | library: 'ReactErrorBoundaries'
24 | },
25 | resolve: {
26 | extensions: ['.js', '.jsx']
27 | },
28 | externals: {
29 | 'react': 'React',
30 | 'react-dom': 'ReactDOM'
31 | },
32 | module: {
33 | rules: [
34 | {
35 | test: /\.js[x]?$/,
36 | include: [
37 | SRC_PATH
38 | ],
39 | loader: 'babel-loader'
40 | }
41 | ]
42 | },
43 | plugins: [
44 | new webpack.optimize.UglifyJsPlugin({
45 | compress: {
46 | warnings: false
47 | },
48 | beautify: true,
49 | comments: true,
50 | mangle: false
51 | }),
52 | new webpack.BannerPlugin('Copyright © 2017 by Chyrain. All rights reserved.')
53 | ]
54 | }
--------------------------------------------------------------------------------
/webpack.dev.config.js:
--------------------------------------------------------------------------------
1 | // webpack.config.js
2 | const path = require("path");
3 | const webpack = require("webpack");
4 | const HtmlwebpackPlugin = require("html-webpack-plugin");
5 | const OpenBrowserPlugin = require("open-browser-webpack-plugin");
6 |
7 | const SRC_PATH = path.resolve(__dirname, "src");
8 | const BUILD_PATH = path.resolve(__dirname, "build");
9 | const EXAMPLE_PATH = path.resolve(__dirname, "example");
10 | const EXAMPLE_SRC_PATH = path.resolve(__dirname, "example/src");
11 |
12 | module.exports = {
13 | // Source Maps("source-map|cheap-module-source-map|eval-source-map|cheap-module-eval-source-map")
14 | devtool: "cheap-module-source-map",
15 | entry: {
16 | // webpack: [
17 | // "webpack-dev-server/client?http://0.0.0.0:8080",
18 | // "webpack/hot/only-dev-server"
19 | // ],
20 | index: path.resolve(EXAMPLE_SRC_PATH, "index.jsx")
21 | },
22 | output: {
23 | path: BUILD_PATH,
24 | filename: "[name].js"
25 | },
26 | resolve: {
27 | extensions: [".js", ".jsx"]
28 | },
29 | module: {
30 | rules: [
31 | {
32 | test: /\.js[x]?$/,
33 | include: [SRC_PATH, EXAMPLE_SRC_PATH],
34 | loader: "babel-loader"
35 | }
36 | ]
37 | },
38 | plugins: [
39 | new webpack.DefinePlugin({
40 | "process.env": {
41 | NODE_ENV: '"production"',
42 | ERROR_ENV: '"development"'
43 | }
44 | }),
45 | // new webpack.optimize.UglifyJsPlugin({
46 | // compress: {
47 | // warnings: false
48 | // },
49 | // sourceMap: true
50 | // }),
51 | new OpenBrowserPlugin({ url: "http://localhost:8080" }),
52 | new webpack.BannerPlugin(
53 | "Copyright © 2017 by Chyrain. All rights reserved."
54 | ),
55 | new HtmlwebpackPlugin({
56 | template: path.resolve(EXAMPLE_PATH, "./index.html"),
57 | filename: "index.html",
58 | inject: "body"
59 | })
60 | ]
61 | };
62 |
--------------------------------------------------------------------------------