-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat!: add componentDidCatch for error handling #60
Changes from 3 commits
36ee519
3987423
2dd18d6
05e2c6d
8926608
3b31ef8
53f9bdb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ sub init() | |
m.state = {} | ||
m.elementToFocus = Invalid | ||
|
||
m._enabledErrorHandling = Type(componentDidCatch) <> "<uninitialized>" | ||
m._isInitialized = false | ||
m._previousProps = {} | ||
m._previousState = {} | ||
|
@@ -21,7 +22,8 @@ sub initKopytko(dynamicProps = {} as Object, componentsMapping = {} as Object) | |
m.top.observeFieldScoped("focusedChild", "focusDidChange") | ||
m.top.update(dynamicProps) | ||
|
||
constructor() | ||
_methodCall(constructor, "constructor") | ||
|
||
m._previousState = _cloneObject(m.state) ' required because of setting default state in constructor() | ||
|
||
_mountComponent() | ||
|
@@ -32,7 +34,7 @@ end sub | |
sub destroyKopytko(data = {} as Object) | ||
if (NOT m._isInitialized) then return | ||
|
||
componentWillUnmount() | ||
_methodCall(componentWillUnmount, "componentWillUnmount") | ||
|
||
if (m["$$eventBus"] <> Invalid) | ||
m["$$eventBus"].clear() | ||
|
@@ -41,7 +43,8 @@ sub destroyKopytko(data = {} as Object) | |
m.state = {} | ||
m._previousState = {} | ||
m.top.unobserveFieldScoped("focusedChild") | ||
m._kopytkoUpdater.destroy() | ||
|
||
_methodCall(m._kopytkoUpdater.destroy, "destroyKopytko", [], m._kopytkoUpdater) | ||
|
||
_clearDOM() | ||
|
||
|
@@ -73,15 +76,15 @@ sub focusDidChange(event as Object) | |
end sub | ||
|
||
sub setState(partialState as Object, callback = Invalid as Dynamic) | ||
m._kopytkoUpdater.enqueueStateUpdate(partialState, callback) | ||
_methodCall(m._kopytkoUpdater.enqueueStateUpdate, "setState", [partialState, callback], m._kopytkoUpdater) | ||
end sub | ||
|
||
sub forceUpdate() | ||
m._kopytkoUpdater.forceStateUpdate() | ||
_methodCall(m._kopytkoUpdater.forceStateUpdate, "forceUpdate", [], m._kopytkoUpdater) | ||
end sub | ||
|
||
sub enqueueUpdate() | ||
m._kopytkoUpdater.enqueueStateUpdate() | ||
_methodCall(m._kopytkoUpdater.enqueueStateUpdate, "enqueueUpdate", [], m._kopytkoUpdater) | ||
end sub | ||
|
||
sub updateProps(props = {} as Object) | ||
|
@@ -92,10 +95,10 @@ end sub | |
|
||
sub _mountComponent() | ||
m._virtualDOM = render() | ||
m._kopytkoDOM.renderElement(m._virtualDOM, m.top) | ||
|
||
m._kopytkoUpdater.setComponentMounted(m.state) | ||
componentDidMount() | ||
_methodCall(m._kopytkoDOM.renderElement, "renderElement", [m._virtualDOM, m.top], m._kopytkoDOM) | ||
_methodCall(m._kopytkoUpdater.setComponentMounted, "setComponentMounted", [m.state], m._kopytkoUpdater) | ||
_methodCall(componentDidMount, "componentDidMount") | ||
end sub | ||
|
||
sub _onStateUpdated() | ||
|
@@ -114,7 +117,8 @@ sub _updateDOM() | |
m.top.setFocus(true) | ||
end if | ||
|
||
componentDidUpdate(m._previousProps, m._previousState) | ||
_methodCall(componentDidUpdate, "componentDidUpdate", [m._previousProps, m._previousState]) | ||
|
||
m._previousState = _cloneObject(m.state) | ||
end sub | ||
|
||
|
@@ -131,3 +135,39 @@ function _cloneObject(obj as Object) as Object | |
|
||
return newObj | ||
end function | ||
|
||
sub _methodCall(func as Function, methodName as String, args = [] as Object, context = Invalid as Object) | ||
if (m._enabledErrorHandling) | ||
try | ||
_functionCall(func, args, context) | ||
catch error | ||
_throw(error, methodName) | ||
end try | ||
else | ||
_functionCall(func, args, context) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Less nesting if return is appended and no need for else. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need for a I can change to return. It doesn't matter to me. |
||
end if | ||
end sub | ||
|
||
sub _functionCall(func as Function, args = [] as Object, context = Invalid as Object) | ||
if (context = Invalid) then context = GetGlobalAA() | ||
|
||
argumentsNumber = args.count() | ||
context["$$functionCall"] = func | ||
|
||
if (argumentsNumber = 0) | ||
context["$$functionCall"]() | ||
else if (argumentsNumber = 1) | ||
context["$$functionCall"](args[0]) | ||
else if (argumentsNumber = 2) | ||
context["$$functionCall"](args[0], args[1]) | ||
end if | ||
|
||
context.delete("$$functionCall") | ||
end sub | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. kopytko utils have function call function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, will add utils function |
||
|
||
sub _throw(error as Object, failingComponentMethod as String) | ||
componentDidCatch(error, { | ||
componentMethod: failingComponentMethod, | ||
componentName: m.top.subtype(), | ||
}) | ||
end sub |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think?
Without bs_const I won't be able to catch original error (during development for example). Working with supressed or rethrown errors might be a nightmare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer Handling, but it doesn't matter to me so much, I can change to Catching.
But if someone is suppressing the error for some reason, it shouldn't crash.
Some app components could always extend KopytkoGroup and have any logic the dev wants, it could be embedded there, so for example it could throw an error if they want.
And use this component instead of the KopytkoGroup.
For example, JavaScript suppressed error won't be thrown.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tomek-r
For example, with the current solution, if you want to handle an error but have it thrown for the dev environment, you can create your component that will extend KopytkoGroup and use the MyAppGroup component instead of KopytkoGroup.
Of course, it won't be the ideal solution as the backtrace of the crash won't be full,
but you still have the original error message and the original line that crashed.
The app is stopped also in the component scope (8085 port), so maybe you are not exactly at the line of the function that crashed, but you can check the component context and variables.
It is a trade-off solution I won't argue with that, but it is also an optional one, I don't want to force anybody to use it.
Maybe I will have some idea in the future how it could be improved but, don't have it now.