Skip to content

Commit cf8af7b

Browse files
P0lipbilliegoose
authored andcommitted
feat: make JSV recoverable from error state (#28)
1 parent ae9cb8c commit cf8af7b

File tree

3 files changed

+44
-19
lines changed

3 files changed

+44
-19
lines changed

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@
4848
"classnames": "^2.2.6",
4949
"json-schema-merge-allof": "^0.6.0",
5050
"mobx-react-lite": "^1.3.1",
51-
"pluralize": "^7.0.0",
52-
"react-error-boundary": "^1.2.5"
51+
"pluralize": "^7.0.0"
5352
},
5453
"devDependencies": {
5554
"@sambego/storybook-state": "^1.3.4",

src/__stories__/JsonSchemaViewer.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react';
22

33
import { State, Store } from '@sambego/storybook-state';
44
import { action } from '@storybook/addon-actions';
5-
import { boolean, number, object, text, withKnobs } from '@storybook/addon-knobs';
5+
import { boolean, number, object, select, text, withKnobs } from '@storybook/addon-knobs';
66
import { storiesOf } from '@storybook/react';
77
import { JsonSchemaViewer } from '../components';
88

@@ -82,7 +82,14 @@ storiesOf('JsonSchemaViewer', module)
8282
<JsonSchemaViewer
8383
name={text('name', 'throw me an error!')}
8484
// @ts-ignore
85-
schema={null}
85+
schema={select(
86+
'schema',
87+
{
88+
'null (throws error)': null,
89+
'object (recovers from error)': schema,
90+
},
91+
null,
92+
)}
8693
onError={(error: any) => console.log('You can hook into the onError handler too!', error)}
8794
expanded={boolean('expanded', false)}
8895
defaultExpandedDepth={number('defaultExpandedDepth', 2)}

src/components/JsonSchemaViewer.tsx

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import { TreeStore } from '@stoplight/tree-list';
22
import * as cn from 'classnames';
33
import { runInAction } from 'mobx';
44
import * as React from 'react';
5-
import ErrorBoundary, { ErrorBoundaryProps, FallbackProps } from 'react-error-boundary';
65

76
import { JSONSchema4 } from 'json-schema';
87
import { GoToRefHandler } from '../types';
98
import { isSchemaViewerEmpty, renderSchema } from '../utils';
109
import { SchemaTree } from './SchemaTree';
1110

12-
export interface IJsonSchemaViewer extends ErrorBoundaryProps {
11+
export type FallbackComponent = React.ComponentType<{ error: Error | null }>;
12+
13+
export interface IJsonSchemaViewer {
1314
schema: JSONSchema4;
1415
dereferencedSchema?: JSONSchema4;
1516
style?: object;
@@ -21,6 +22,7 @@ export interface IJsonSchemaViewer extends ErrorBoundaryProps {
2122
hideTopBar?: boolean;
2223
maxRows?: number;
2324
onGoToRef?: GoToRefHandler;
25+
FallbackComponent?: FallbackComponent;
2426
}
2527

2628
export class JsonSchemaViewerComponent extends React.PureComponent<IJsonSchemaViewer> {
@@ -86,7 +88,7 @@ export class JsonSchemaViewerComponent extends React.PureComponent<IJsonSchemaVi
8688
}
8789
}
8890

89-
const JsonSchemaFallbackComponent: React.FunctionComponent<FallbackProps> = ({ error }) => {
91+
const JsonSchemaFallbackComponent: FallbackComponent = ({ error }) => {
9092
return (
9193
<div className="p-4">
9294
<b>Error</b>
@@ -95,15 +97,32 @@ const JsonSchemaFallbackComponent: React.FunctionComponent<FallbackProps> = ({ e
9597
);
9698
};
9799

98-
export const JsonSchemaViewer: React.FunctionComponent<IJsonSchemaViewer> = ({
99-
onError,
100-
FallbackComponent = JsonSchemaFallbackComponent,
101-
...props
102-
}) => {
103-
return (
104-
<ErrorBoundary onError={onError} FallbackComponent={FallbackComponent}>
105-
<JsonSchemaViewerComponent {...props} />
106-
</ErrorBoundary>
107-
);
108-
};
109-
JsonSchemaViewer.displayName = 'JsonSchemaViewer';
100+
// react-error-boundary does not support recovering, see https://github.com/bvaughn/react-error-boundary/pull/16/files
101+
export class JsonSchemaViewer extends React.PureComponent<IJsonSchemaViewer, { error: null | Error }> {
102+
public state = {
103+
error: null,
104+
};
105+
106+
public componentDidUpdate(prevProps: Readonly<IJsonSchemaViewer>) {
107+
if (
108+
this.state.error !== null &&
109+
(prevProps.schema !== this.props.schema || prevProps.dereferencedSchema !== this.props.dereferencedSchema)
110+
) {
111+
this.setState({ error: null });
112+
}
113+
}
114+
115+
// there is no error hook yet, see https://reactjs.org/docs/hooks-faq.html#how-do-lifecycle-methods-correspond-to-hooks
116+
public static getDerivedStateFromError(error: Error) {
117+
return { error };
118+
}
119+
120+
public render() {
121+
const { FallbackComponent: Fallback = JsonSchemaFallbackComponent, ...props } = this.props;
122+
if (this.state.error) {
123+
return <Fallback error={this.state.error} />;
124+
}
125+
126+
return <JsonSchemaViewerComponent {...props} />;
127+
}
128+
}

0 commit comments

Comments
 (0)