feat: pass error in init callback + onLoaded for RedocStandalone

This commit is contained in:
Roman Hotsiy 2018-06-25 13:48:51 +03:00
parent 637b493b41
commit 16313b8293
No known key found for this signature in database
GPG Key ID: 5CB7B3ACABA57CB0
4 changed files with 31 additions and 15 deletions

View File

@ -11,9 +11,10 @@ export interface RedocStandaloneProps {
spec?: object; spec?: object;
specUrl?: string; specUrl?: string;
options?: RedocRawOptions; options?: RedocRawOptions;
onLoaded?: (e?: Error) => any;
} }
export class RedocStandalone extends React.Component<RedocStandaloneProps> { export class RedocStandalone extends React.PureComponent<RedocStandaloneProps> {
static propTypes = { static propTypes = {
spec: (props, _, componentName) => { spec: (props, _, componentName) => {
if (!props.spec && !props.specUrl) { if (!props.spec && !props.specUrl) {
@ -36,14 +37,14 @@ export class RedocStandalone extends React.Component<RedocStandaloneProps> {
}; };
render() { render() {
const { spec, specUrl, options = {} } = this.props; const { spec, specUrl, options = {}, onLoaded } = this.props;
const hideLoading = options.hideLoading !== undefined; const hideLoading = options.hideLoading !== undefined;
const normalizedOpts = new RedocNormalizedOptions(options); const normalizedOpts = new RedocNormalizedOptions(options);
return ( return (
<ErrorBoundary> <ErrorBoundary>
<StoreProvider spec={spec} specUrl={specUrl} options={options}> <StoreProvider spec={spec} specUrl={specUrl} options={options} onLoaded={onLoaded}>
{({ loading, store }) => {({ loading, store }) =>
!loading ? ( !loading ? (
<Redoc store={store!} /> <Redoc store={store!} />

View File

@ -13,22 +13,27 @@ export interface StoreProviderProps {
options?: RedocRawOptions; options?: RedocRawOptions;
onLoaded?: (e?: Error) => void;
children: (props: { loading: boolean; store?: AppStore }) => any; children: (props: { loading: boolean; store?: AppStore }) => any;
} }
export interface StoreProviderState { export interface StoreProviderState {
error?: Error; error?: Error;
loading: boolean; loading: boolean;
spec?: any; resolvedSpec?: any;
prevSpec?: any;
prevSpecUrl?: string; prevSpecUrl?: string;
} }
export class StoreProvider extends Component<StoreProviderProps, StoreProviderState> { export class StoreProvider extends Component<StoreProviderProps, StoreProviderState> {
static getDerivedStateFromProps(props, state: StoreProviderState) { static getDerivedStateFromProps(nextProps: StoreProviderProps, prevState: StoreProviderState) {
if (props.specUrl !== state.prevSpecUrl) { if (nextProps.specUrl !== prevState.prevSpecUrl || nextProps.spec !== prevState.prevSpec) {
return { return {
spec: null, loading: true,
prevSpecUrl: props.specUrl, resolvedSpec: null,
prevSpec: nextProps.spec,
prevSpecUrl: nextProps.specUrl,
}; };
} }
@ -37,7 +42,7 @@ export class StoreProvider extends Component<StoreProviderProps, StoreProviderSt
state: StoreProviderState = { state: StoreProviderState = {
loading: true, loading: true,
spec: null, resolvedSpec: null,
}; };
@memoize @memoize
@ -53,8 +58,11 @@ export class StoreProvider extends Component<StoreProviderProps, StoreProviderSt
} }
componentDidUpdate() { componentDidUpdate() {
if (this.props.spec === null) { if (this.state.resolvedSpec === null) {
this.load(); this.load();
} else if (!this.state.loading && this.props.onLoaded) {
// may run multiple time
this.props.onLoaded();
} }
} }
@ -62,8 +70,11 @@ export class StoreProvider extends Component<StoreProviderProps, StoreProviderSt
const { specUrl, spec, options } = this.props; const { specUrl, spec, options } = this.props;
try { try {
const resolvedSpec = await loadAndBundleSpec(spec || specUrl!); const resolvedSpec = await loadAndBundleSpec(spec || specUrl!);
this.setState({ spec: resolvedSpec, loading: false }); this.setState({ resolvedSpec, loading: false });
} catch (e) { } catch (e) {
if (this.props.onLoaded) {
this.props.onLoaded(e);
}
this.setState({ error: e }); this.setState({ error: e });
} }
} }
@ -74,10 +85,10 @@ export class StoreProvider extends Component<StoreProviderProps, StoreProviderSt
} }
const { specUrl, options } = this.props; const { specUrl, options } = this.props;
const { loading, spec } = this.state; const { loading, resolvedSpec } = this.state;
return this.props.children({ return this.props.children({
loading, loading,
store: this.makeStore(spec, specUrl, options), store: this.makeStore(resolvedSpec, specUrl, options),
}); });
} }
} }

View File

@ -37,7 +37,7 @@ export function init(
specOrSpecUrl: string | any, specOrSpecUrl: string | any,
options: any = {}, options: any = {},
element: Element | null = querySelector('redoc'), element: Element | null = querySelector('redoc'),
callback?: () => void, callback?: (e?: Error) => void,
) { ) {
if (element === null) { if (element === null) {
throw new Error('"element" argument is not provided and <redoc> tag is not found on the page'); throw new Error('"element" argument is not provided and <redoc> tag is not found on the page');
@ -57,13 +57,13 @@ export function init(
RedocStandalone, RedocStandalone,
{ {
spec, spec,
onLoaded: callback,
specUrl, specUrl,
options: { ...options, ...parseOptionsFromElement(element) }, options: { ...options, ...parseOptionsFromElement(element) },
}, },
['Loading...'], ['Loading...'],
), ),
element, element,
callback,
); );
} }

View File

@ -5914,6 +5914,10 @@ mem@^1.1.0:
dependencies: dependencies:
mimic-fn "^1.0.0" mimic-fn "^1.0.0"
memoize-one@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-3.1.1.tgz#ef609811e3bc28970eac2884eece64d167830d17"
memory-fs@^0.4.0, memory-fs@~0.4.1: memory-fs@^0.4.0, memory-fs@~0.4.1:
version "0.4.1" version "0.4.1"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"