preact-batteries
Version:
Batteries for you projects with Preact, TypeScript (or Babel)
92 lines (78 loc) • 2.68 kB
text/typescript
import {
CompatibleComponentInterface,
ComponentModule
} from "./module-system";
class RenderMethodNotDefinedError extends Error {
constructor() {
super(
"[preact-batteries.trouble-free-component]: render method in your component is not defined"
);
}
}
/**
* This function allows you to easily wrap your method with
* try catch which will trigger your onError listener, which will
* be called then within a context of class. Useful when you want intercept
* errors among several (or all) methods of your class
*/
export function listenComponentMethodForErrors(
context,
methodName: string,
onError?: (e: Error) => void
) {
const old: Function | undefined = context[methodName];
if (methodName === "render" && !old) {
throw new RenderMethodNotDefinedError();
}
context[methodName] = function(...args) {
try {
if (methodName === "render") {
return old.call(context, ...args);
}
old && old.call(context, ...args);
} catch (e) {
if (onError) {
onError.call(context, e);
}
}
}.bind(context);
}
const ERROR_MESSAGE = "[CRITICAL ERROR]";
function renderFunction() {
return ERROR_MESSAGE;
}
function onErrorFunc(e: Error | string) {
this.render = renderFunction;
console.error(
`[preact-batteries.troube-free-component]: you had error in one of your methods in component.
You can distinguish one with error by looking at your interface. It should show ${ERROR_MESSAGE}. Also, here is
error message. Logging error after this message.`
);
if (e instanceof Error) {
console.error(e.stack);
} else {
console.error(e);
}
}
function onAppearedErrorFunc(e: Error) {
if (this.forceUpdate) {
onErrorFunc.call(this, e);
this.setState({ __error_state_7: Math.random() * 1000 });
} else {
console.warn(
`[preact-batteries.trouble-free-component]: component did not have forceUpdate method,
and error appeared in the render method or didMount method. So error in the component cannot be shown in the interface.
Throwing error further.`
);
throw e;
}
}
export class TroubleFreeComponent implements ComponentModule<CompatibleComponentInterface<any, any>> {
public init(c: CompatibleComponentInterface<any, any>) {
listenComponentMethodForErrors(c, "componentWillMount", onErrorFunc);
listenComponentMethodForErrors(c, "componentDidMount", onAppearedErrorFunc);
listenComponentMethodForErrors(c, "componentWillReceiveProps", onErrorFunc);
listenComponentMethodForErrors(c, "shouldComponentUpdate", onErrorFunc);
listenComponentMethodForErrors(c, "render", onAppearedErrorFunc);
}
}