slate
slate copied to clipboard
Slate errors cannot be caught by error boundaries and crash the whole app
For some reason, currently, slate errors crash the whole app and cannot be caught by https://reactjs.org/docs/error-boundaries.html
Need a minimum reproduction. Also, please fill out the template, it's there for a reason.
I think this occurs because React error boundaries will only catch errors that occur in the render cycle and not errors in event-handlers? We have experienced this issue too. It would be nice to use a React error boundary to catch all Slate errors, but I'm not sure it's possible.
Unfortunately, may have to wrap each Slate call in a try/catch, which is super cumbersome, unless you have another suggestion @helios1138? Really, I am not so sure that Slate should be throwing so many errors in the codebase. It may be a bit of an anti-pattern and the code might benefit from returning null
vs. throwing errors in a number of locations. But that requires some more thought/discussion...
Unfortunately, may have to wrap each Slate call in a try/catch
I actually just created a plugin to do this. It was surprisingly simple.
import { logError } from '~/utils/logError';
const tryCatchCallback =
(callback) =>
(...args) => {
try {
return callback(...args);
} catch (error) {
logError(error);
throw error;
}
};
export const withErrorReporting = (editor) => {
Object.entries(editor).forEach(([key, value]) => {
if (typeof value === 'function') {
editor[key] = tryCatchCallback(value);
}
});
return editor;
};
You just need to make sure it's the outermost plugin, so that it runs first. Works like a charm.
Edit: See any problems with a solution like this?
@guldenmw thanks alot for sharing your simple yet very helpful Slate error-handling plugin. works great, while React ErrorBoundary utilizing componentDidCatch
did not
I am calling editor.undo()
in case of error. I yet have to test it in real-life conditions for undesired side-effects.
import { Editor } from 'slate'
// tries to invoke editorFunc. in case of error, it undoes the last action and captures exception
const tryCatchCallback =
(editorFunc: any, editor: Editor) =>
(...editorFuncArgs: any) => {
try {
return editorFunc(...editorFuncArgs)
} catch (error) {
captureException(error as Error)
editor.undo()
}
}
// wraps all editor functions with our tryCatchCallback
export const withErrorReporting = (editor: any): Editor => {
Object.entries(editor).forEach(([key, value]) => {
if (typeof value === 'function') {
editor[key] = tryCatchCallback(value, editor)
}
})
return editor as Editor
}