add error catch

This commit is contained in:
kangwei 2020-05-07 22:12:37 +08:00
parent fb918a10be
commit db35986635
2 changed files with 223 additions and 153 deletions

View File

@ -197,168 +197,178 @@ export default class BaseEngine extends PureComponent {
// parentInfo schemaComp
// idx Index
__createVirtualDom = (schema, self, parentInfo, idx) => {
if (!schema) return null;
const { __appHelper: appHelper, __components: components = {} } =
this.props || {};
const { engine } = this.context || {};
if (isJSExpression(schema)) {
return parseExpression(schema, self);
}
if (typeof schema === 'string') return schema;
if (typeof schema === 'number' || typeof schema === 'boolean') {
return schema.toString();
}
if (Array.isArray(schema)) {
if (schema.length === 1) return this.__createVirtualDom(schema[0], self, parentInfo);
return schema.map((item, idx) =>
this.__createVirtualDom(item, self, parentInfo, item && item.__ctx && item.__ctx.lunaKey ? '' : idx),
);
}
try {
if (!schema) return null;
const { __appHelper: appHelper, __components: components = {} } =
this.props || {};
const { engine } = this.context || {};
if (isJSExpression(schema)) {
return parseExpression(schema, self);
}
if (typeof schema === 'string') return schema;
if (typeof schema === 'number' || typeof schema === 'boolean') {
return schema.toString();
}
if (Array.isArray(schema)) {
if (schema.length === 1) return this.__createVirtualDom(schema[0], self, parentInfo);
return schema.map((item, idx) =>
this.__createVirtualDom(item, self, parentInfo, item && item.__ctx && item.__ctx.lunaKey ? '' : idx),
);
}
//
if (schema.componentName === 'Flagment' && schema.children) {
let tarChildren = isJSExpression(schema.children) ? parseExpression(schema.children, self) : schema.children;
return this.__createVirtualDom(tarChildren, self, parentInfo);
}
//
if (schema.componentName === 'Flagment' && schema.children) {
let tarChildren = isJSExpression(schema.children) ? parseExpression(schema.children, self) : schema.children;
return this.__createVirtualDom(tarChildren, self, parentInfo);
}
if (schema.$$typeof) {
return schema;
}
if (!isSchema(schema)) return null;
if (schema.$$typeof) {
return schema;
}
if (!isSchema(schema)) return null;
let Comp = components[schema.componentName] || Div;
let Comp = components[schema.componentName] || engine.getNotFoundComponent();
if (schema.hidden) {
return null;
}
if (schema.hidden) {
return null;
}
if (schema.loop !== undefined) {
return this.__createLoopVirtualDom(
{
...schema,
loop: parseData(schema.loop, self),
if (schema.loop !== undefined) {
return this.__createLoopVirtualDom(
{
...schema,
loop: parseData(schema.loop, self),
},
self,
parentInfo,
idx,
);
}
const condition = schema.condition === undefined ? true : parseData(schema.condition, self);
if (!condition) return null;
let scopeKey = '';
// scopethis.__compScopes
if (Comp.generateScope) {
const key = parseExpression(schema.props.key, self);
if (key) {
// key使key
scopeKey = key;
} else if (!schema.__ctx) {
// schema__ctxlunaKey
schema.__ctx = {
lunaKey: `luna${++scopeIdx}`,
};
scopeKey = schema.__ctx.lunaKey;
} else {
//
scopeKey = schema.__ctx.lunaKey + (idx !== undefined ? `_${idx}` : '');
}
if (!this.__compScopes[scopeKey]) {
this.__compScopes[scopeKey] = Comp.generateScope(this, schema);
}
}
// scopescope
if (scopeKey && this.__compScopes[scopeKey]) {
const compSelf = { ...this.__compScopes[scopeKey] };
compSelf.__proto__ = self;
self = compSelf;
}
// propscontext
const otherProps = isFileSchema(schema)
? {
__schema: schema,
__appHelper: appHelper,
__components: components,
}
: {};
if (engine && engine.props.designMode) {
otherProps.__designMode = engine.props.designMode;
}
const componentInfo = {};
const props = this.__parseProps(schema.props, self, '', {
schema,
Comp,
componentInfo: {
...componentInfo,
props: transformArrayToMap(componentInfo.props, 'name'),
},
});
// ref
if (acceptsRef(Comp)) {
otherProps.ref = (ref) => {
const refProps = props.ref;
if (refProps && typeof refProps === 'string') {
this[refProps] = ref;
}
engine && engine.props.onCompGetRef(schema, ref);
};
}
// scope
if (scopeKey && this.__compScopes[scopeKey]) {
props.__scope = this.__compScopes[scopeKey];
}
if (schema.__ctx && schema.__ctx.lunaKey) {
if (!isFileSchema(schema)) {
engine && engine.props.onCompGetCtx(schema, self);
}
props.key = props.key || `${schema.__ctx.lunaKey}_${schema.__ctx.idx || 0}_${idx !== undefined ? idx : ''}`;
} else if (typeof idx === 'number' && !props.key) {
props.key = idx;
}
props.__id = schema.id;
const renderComp = (props) => {
return engine.createElement(
Comp,
props,
(!isFileSchema(schema) &&
!!schema.children &&
this.__createVirtualDom(
isJSExpression(schema.children) ? parseExpression(schema.children, self) : schema.children,
self,
{
schema,
Comp,
},
)) ||
null,
);
};
//
if (engine && [DESIGN_MODE.EXTEND, DESIGN_MODE.BORDER].includes(engine.props.designMode)) {
//overlay,dialog使div
if (OVERLAY_LIST.includes(schema.componentName)) {
const { ref, ...overlayProps } = otherProps;
return (
<Div ref={ref} __designMode={engine.props.designMode}>
{renderComp({ ...props, ...overlayProps })}
</Div>
);
}
// dom
if (componentInfo && componentInfo.parentRule) {
const parentList = componentInfo.parentRule.split(',');
const { schema: parentSchema, Comp: parentComp } = parentInfo;
if (!parentList.includes(parentSchema.componentName) || parentComp !== components[parentSchema.componentName]) {
props.__componentName = schema.componentName;
Comp = VisualDom;
} else {
// dom
props.__disableDesignMode = true;
}
}
}
return renderComp({ ...props, ...otherProps });
} catch (e) {
return engine.createElement(engine.getFaultComponent(), {
error: e,
schema,
self,
parentInfo,
idx,
);
});
}
const condition = schema.condition === undefined ? true : parseData(schema.condition, self);
if (!condition) return null;
let scopeKey = '';
// scopethis.__compScopes
if (Comp.generateScope) {
const key = parseExpression(schema.props.key, self);
if (key) {
// key使key
scopeKey = key;
} else if (!schema.__ctx) {
// schema__ctxlunaKey
schema.__ctx = {
lunaKey: `luna${++scopeIdx}`,
};
scopeKey = schema.__ctx.lunaKey;
} else {
//
scopeKey = schema.__ctx.lunaKey + (idx !== undefined ? `_${idx}` : '');
}
if (!this.__compScopes[scopeKey]) {
this.__compScopes[scopeKey] = Comp.generateScope(this, schema);
}
}
// scopescope
if (scopeKey && this.__compScopes[scopeKey]) {
const compSelf = { ...this.__compScopes[scopeKey] };
compSelf.__proto__ = self;
self = compSelf;
}
// propscontext
const otherProps = isFileSchema(schema)
? {
__schema: schema,
__appHelper: appHelper,
__components: components,
}
: {};
if (engine && engine.props.designMode) {
otherProps.__designMode = engine.props.designMode;
}
const componentInfo = {};
const props = this.__parseProps(schema.props, self, '', {
schema,
Comp,
componentInfo: {
...componentInfo,
props: transformArrayToMap(componentInfo.props, 'name'),
},
});
// ref
if (acceptsRef(Comp)) {
otherProps.ref = (ref) => {
const refProps = props.ref;
if (refProps && typeof refProps === 'string') {
this[refProps] = ref;
}
engine && engine.props.onCompGetRef(schema, ref);
};
}
// scope
if (scopeKey && this.__compScopes[scopeKey]) {
props.__scope = this.__compScopes[scopeKey];
}
if (schema.__ctx && schema.__ctx.lunaKey) {
if (!isFileSchema(schema)) {
engine && engine.props.onCompGetCtx(schema, self);
}
props.key = props.key || `${schema.__ctx.lunaKey}_${schema.__ctx.idx || 0}_${idx !== undefined ? idx : ''}`;
} else if (typeof idx === 'number' && !props.key) {
props.key = idx;
}
props.__id = schema.id;
const renderComp = (props) => {
return engine.createElement(
Comp,
props,
(!isFileSchema(schema) &&
!!schema.children &&
this.__createVirtualDom(
isJSExpression(schema.children) ? parseExpression(schema.children, self) : schema.children,
self,
{
schema,
Comp,
},
)) ||
null,
);
};
//
if (engine && [DESIGN_MODE.EXTEND, DESIGN_MODE.BORDER].includes(engine.props.designMode)) {
//overlay,dialog使div
if (OVERLAY_LIST.includes(schema.componentName)) {
const { ref, ...overlayProps } = otherProps;
return (
<Div ref={ref} __designMode={engine.props.designMode}>
{renderComp({ ...props, ...overlayProps })}
</Div>
);
}
// dom
if (componentInfo && componentInfo.parentRule) {
const parentList = componentInfo.parentRule.split(',');
const { schema: parentSchema, Comp: parentComp } = parentInfo;
if (!parentList.includes(parentSchema.componentName) || parentComp !== components[parentSchema.componentName]) {
props.__componentName = schema.componentName;
Comp = VisualDom;
} else {
// dom
props.__disableDesignMode = true;
}
}
}
return renderComp({ ...props, ...otherProps });
};
__createLoopVirtualDom = (schema, self, parentInfo, idx) => {

View File

@ -1,4 +1,4 @@
import React, { PureComponent, createElement as reactCreateElement } from 'react';
import React, { Component, PureComponent, createElement as reactCreateElement } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import Debug from 'debug';
@ -11,6 +11,7 @@ import AddonEngine from './addonEngine';
import TempEngine from './tempEngine';
import { isEmpty } from '@ali/b3-one/lib/obj';
import BaseEngine from './base';
import Div from '@ali/iceluna-comp-div';
window.React = React;
window.ReactDom = ReactDOM;
@ -23,6 +24,26 @@ const ENGINE_COMPS = {
AddonEngine,
TempEngine,
};
class FaultComponent extends PureComponent {
render() {
// FIXME: errorlog
console.error('render error', this.props);
return <Div>RenderError</Div>;
}
}
class NotFoundComponent extends PureComponent {
render() {
console.error('component not found', this.props);
return <Div {...this.props} />;
}
}
function isReactClass(obj) {
return obj && obj.prototype && (obj.prototype.isReactComponent || obj.prototype instanceof Component);
}
export default class Engine extends PureComponent {
static dislayName = 'engine';
static propTypes = {
@ -86,9 +107,48 @@ export default class Engine extends PureComponent {
}
};
patchDidCatch(Component) {
if (!isReactClass(Component)) {
return;
}
if (Component.patchedCatch) {
return;
}
Component.patchedCatch = true;
Component.getDerivedStateFromError = (error) => {
return { engineRenderError: true, error };
};
const engine = this;
const originRender = Component.prototype.render;
Component.prototype.render = function () {
if (this.state && this.state.engineRenderError) {
return engine.createElement(this.getFaultComponent(), {
error: this.state.error,
props: this.props,
});
}
return originRender.call(this);
};
const originShouldComponentUpdate = Component.prototype.shouldComponentUpdate;
Component.prototype.shouldComponentUpdate = function (nextProps, nextState) {
if (nextState && nextState.engineRenderError) {
return true;
}
return originShouldComponentUpdate ? originShouldComponentUpdate.call(this, nextProps, nextState) : true;
};
}
createElement(Component, props, children) {
// TODO: enable in runtime mode?
this.patchDidCatch(Component);
return (this.props.customCreateElement || reactCreateElement)(Component, props, children);
}
getNotFoundComponent() {
return this.props.notFoundComponent || NotFoundComponent;
}
getFaultComponent() {
return this.props.faultComponent || FaultComponent;
}
render() {
const { schema, designMode, appHelper, components, customCreateElement } = this.props;