Merge pull request #8844 from wibetter/master

fix(amis): StaticHoc改用componentDidCatch拦截渲染异常
This commit is contained in:
刘丹 2023-11-22 14:25:28 +08:00 committed by GitHub
commit b326802b1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 106 additions and 52 deletions

View File

@ -0,0 +1,58 @@
/**
* @file ErrorBoundary
* @description JavaScript
* @author wibetter
*/
import React from 'react';
interface ErrorBoundaryProps {
// 自定义错误信息,控制台输出
customErrorMsg?: string;
fallback?: () => void;
children: any;
}
interface ErrorBoundaryStates {
hasError: boolean;
}
export default class ErrorBoundary extends React.Component<
ErrorBoundaryProps,
ErrorBoundaryStates
> {
constructor(props: any) {
super(props);
this.state = {hasError: false};
}
componentDidCatch(error: any, errorInfo: any) {
const {customErrorMsg} = this.props;
if (customErrorMsg) {
console.warn(customErrorMsg);
}
console.warn('错误对象:', error);
console.warn('错误信息:', errorInfo);
this.setState({
hasError: true
});
}
render() {
const {fallback} = this.props;
if (this.state.hasError) {
if (fallback) {
return fallback();
}
// 默认渲染错误信息
return (
<div className="renderer-error-boundary">
</div>
);
}
return this.props.children;
}
}

View File

@ -96,6 +96,7 @@ import type {FilterContext} from 'amis-formula';
import LazyComponent from './components/LazyComponent';
import Overlay from './components/Overlay';
import PopOver from './components/PopOver';
import ErrorBoundary from './components/ErrorBoundary';
import {FormRenderer} from './renderers/Form';
import type {FormHorizontal, FormSchemaBase} from './renderers/Form';
import {
@ -182,6 +183,7 @@ export {
LazyComponent,
Overlay,
PopOver,
ErrorBoundary,
addSchemaFilter,
OptionsControlProps,
FormOptionsControl,

View File

@ -801,14 +801,6 @@
}
}
.ae-Editor-renderer-error {
padding: 5px;
font-family: PingFangSC-Medium;
font-size: 14px;
color: #cf1322;
border: 1px dashed #cf1322;
}
.ae-Editor-tip {
user-select: none;
max-width: 100px;

View File

@ -23,7 +23,8 @@ import {
JSONUpdate,
getFixDialogType
} from '../util';
import {createObject, createObjectFromChain} from 'amis-core';
import {createObjectFromChain} from 'amis-core';
import {ErrorBoundary} from 'amis-core';
import {CommonConfigWrapper} from './CommonConfigWrapper';
import type {Schema} from 'amis';
import type {DataScope} from 'amis-core';
@ -40,14 +41,11 @@ export function makeWrapper(
type Props = RendererProps & {
$$id: string;
};
type States = {
hasError: boolean;
};
const store = manager.store;
const renderer = rendererConfig.component;
@observer
class Wrapper extends React.Component<Props, States> {
class Wrapper extends React.Component<Props> {
static displayName = renderer.displayName;
static propsList = ((renderer && renderer.propsList) || []).concat([
'$$id'
@ -57,11 +55,6 @@ export function makeWrapper(
editorNode?: EditorNodeType;
scopeId?: string;
constructor(props: Props) {
super(props);
this.state = {hasError: false};
}
UNSAFE_componentWillMount() {
const parent: EditorNodeType = (this.context as any) || store.root;
if (!info.id) {
@ -133,16 +126,6 @@ export function makeWrapper(
}
}
componentDidCatch(error: any, errorInfo: any) {
console.warn(`${info.name}(${info.id})渲染发生错误:`);
console.warn('当前渲染器信息:', info);
console.warn('错误对象:', error);
console.warn('错误信息:', errorInfo);
this.setState({
hasError: true
});
}
componentWillUnmount() {
if (this.editorNode && isAlive(this.editorNode)) {
const parent: EditorNodeType = (this.context as any) || store.root;
@ -186,14 +169,6 @@ export function makeWrapper(
render() {
const {$$id, ...rest} = this.props;
if (this.state.hasError) {
return (
<div className="ae-Editor-renderer-error">
{info.name}({info.id})
</div>
);
}
/*
* NodeWrapper ContainerWrapper
* NodeWrapper dom data-editor-id
@ -207,18 +182,31 @@ export function makeWrapper(
: info.regions
? ContainerWrapper
: NodeWrapper; /*)*/
return (
<EditorNodeContext.Provider
value={this.editorNode || (this.context as any)}
>
<Wrapper
{...rest}
render={this.renderChild}
$$editor={info}
$$node={this.editorNode}
ref={this.wrapperRef}
/>
<ErrorBoundary
customErrorMsg={`拦截到${
info.type
}当前组件信息: ${JSON.stringify(this.props.$schema)}`}
fallback={() => {
return (
<div className="renderer-error-boundary">
{info?.type}
</div>
);
}}
>
<Wrapper
{...rest}
render={this.renderChild}
$$editor={info}
$$node={this.editorNode}
ref={this.wrapperRef}
/>
</ErrorBoundary>
</EditorNodeContext.Provider>
);
}

View File

@ -9,3 +9,11 @@
.visibility-sensor {
min-height: 5px;
}
.renderer-error-boundary {
padding: 5px;
font-family: PingFangSC-Medium;
font-size: 14px;
color: #cf1322;
border: 1px dashed #cf1322;
}

View File

@ -67,7 +67,6 @@
"qrcode.react": "^3.1.0",
"react-cropper": "^2.1.8",
"react-dropzone": "^11.4.2",
"react-error-boundary": "^4.0.11",
"react-intersection-observer": "9.5.2",
"react-json-view": "1.21.3",
"react-transition-group": "4.4.2",

View File

@ -1,7 +1,6 @@
import React from 'react';
import toString from 'lodash/toString';
import {getPropValue, FormControlProps} from 'amis-core';
import {ErrorBoundary} from 'react-error-boundary';
import {ErrorBoundary} from 'amis-core';
function renderCommonStatic(props: any, defaultValue: string) {
const {type, render, staticSchema} = props;
@ -135,11 +134,19 @@ export function supportStatic<T extends FormControlProps>() {
}
return (
<div className={cx(`${ns}Form-static`, className)}>
<ErrorBoundary fallback={<>{toString(body)}</>}>
{body}
</ErrorBoundary>
</div>
<ErrorBoundary
customErrorMsg={`拦截到${props.$schema.type}渲染错误当前组件schema: ${props.$schema}`}
fallback={() => {
return (
<div className="renderer-error-boundary">
{props.$schema?.type}
</div>
);
}}
>
<div className={cx(`${ns}Form-static`, className)}>{body}</div>
</ErrorBoundary>
);
}