-
Notifications
You must be signed in to change notification settings - Fork 247
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
Removing the container element after CLOSE/DESTROY zoid event (after close()), results in uncaught error #334
Comments
I've found out about this export function parentComponent<P>(options : NormalizedComponentOptionsType<P>, overrides? : ParentDelegateOverrides<P> = getDefaultOverrides(), parentWin : CrossDomainWindowType = window) : ParentComponent<P> {
// ...
/* onErrorOverride, but seemingly not available for config */
const onErrorOverride : ?OnError = overrides.onError;
// ...
/* the actual onError handler */
const onError = (err : mixed) : ZalgoPromise<void> => {
if (onErrorOverride) {
return onErrorOverride(err);
}
return ZalgoPromise.try(() => {
if (handledErrors.indexOf(err) !== -1) {
return;
}
handledErrors.push(err);
initPromise.asyncReject(err);
return event.trigger(EVENT.ERROR, err);
});
};
// ...
/* the render method. Note that this returns a "ZalgoPromise". Also note that this *asynchronously* handles errors in `.catch(err)`, and afterwards has a `.then()` that throws the error */
const render = (target : CrossDomainWindowType, container : string | HTMLElement, context : $Values<typeof CONTEXT>) : ZalgoPromise<void> => {
return ZalgoPromise.try(() => {
//...
return ZalgoPromise.hash({
initPromise, buildUrlPromise, onRenderPromise, getProxyContainerPromise, openFramePromise, openPrerenderFramePromise, renderContainerPromise, openPromise,
openPrerenderPromise, setStatePromise, prerenderPromise, loadUrlPromise, buildWindowNamePromise, setWindowNamePromise, watchForClosePromise, onDisplayPromise,
openBridgePromise, runTimeoutPromise, onRenderedPromise, delegatePromise, watchForUnloadPromise
});
}).catch(err => {
return ZalgoPromise.all([
onError(err),
destroy(err)
]).then(() => {
throw err; /// (!) here is the throw of the uncaught error !!!
}, () => {
throw err;
});
}).then(noop); Now, when using 'zoidComponent.render()', it's possible to catch errors by adding a
But, for the react driver, it seems to not be possible to do that.
So, the issue seems to be either that:
|
@tmilar Thank you for bringing this up. We have also run into this with our Smart Payment Buttons and needed to give time for the container to be rendered as you have mentioned above. I would recommend setting a delay before calling
Please close if this works for you. |
Hi @mnicpt , thanks for your quick response. It'd be preferable to only wait the actually needed time. For example if it fully loaded, then close it immediately. And if it didn't finish loading, actually wait the right time until it's done. Also, there is the possiblity that an explicit timer might not be enough in some scenarios (ie. slow internet connection) and still throw the error. Now I have 2 questions.
I'm also thinking that an improvement for the react driver could take place, by handling the |
Hi @tmilar, I was reviewing your implementation and was wondering if you could solely rely on the zoid close() function for cleaning up the DOM instead of having the React component do it. Ex: export const MyReactZoidComponent = ({
tag,
url,
...props
}) => {
const Zoid = useMemo(() => {
zoid.destroy()
return zoid.create({ tag, url }).driver("react", { React, ReactDOM })
}, [tag, url])
// return Zoid ? <Zoid {...props} /> : null
// always return the zoid component here no matter what. Let the close() function be responsible for DOM cleanup.
return <Zoid {...props} />
} The zoid close() function should be responsible for removing the DOM of the zoid component. There are some MutationObservers being used with zoid and and calling One other thing to note is the close() function is async. You'll want ensure that is done before having React remove it (ex: // overlay modal close handler
overlay.onclick = () => {
close().then(() => {
console.log('closing successful!');
});
}; |
I really like the idea of using React error boundaries. If anyone wants to take that up, that would be awesome. I'm also debating, should we just make The only concern is, we would be breaking the strong guarantee that |
Hi @gregjopa, thanks for your reply. Regarding this:
It could be possible, but I'd prefer my shared component to have as less limitations as possible. I'd like to allow the option to a remote site of which I might not know anything about, to just remove the component from the view and not have any uncaught errors.
Yes, I've noted it, and attempted to handle it that way. |
@bluepnume, thanks for chiming in. I think a fair solution would be having the Now, regarding this:
-> I'm not very fond of the implementation details. Inspecting the logic I couldn't understand why there has to be an exception on the first place if the user decided to remove/close the component quickly enough. Yes, it's clear that there was an expectation from the zoidComponent to load/render an iframe - but a failure to do that, caused by user deciding to cancel/close/abort it, should not result in an unexpected error I think. My instinct would say to not change/break the API anyway, just would make sure that all possible success/error paths are predictable, and manageable by any user that intends to do so. |
I think that's a fair ask.
So: when you call a) Resolve the promise This is normally a simple decision. If the render succeeds -> resolve the promise. In any other case -> reject the promise. This way, So to answer your question:
It's because anything other than a resolved render promise, is a rejected render promise. And any unhandled, rejected promise, ends up getting displayed as a red error message in the console.
So yeah, giving a way for the React driver, and the user of the react driver, to handle errors during render, definitely sounds like a good idea, no matter which way we go with (Actually, if it's the case that
This would be my instinct too, normally. But I am concerned about this "close during render" case. At PayPal we see this happening a lot -- it's very easy for DOM elements to be removed for a lot of reasons, and a lot of clients who consume zoid components don't handle rejected Maybe we just accept that, so long as there is technically a way for clients to handle those errors. Maybe we make "close during render" a non-rejecting case. Maybe we reject the render promise, but make it some kind of "silent reject" where it doesn't get propagated to the console if it happens. Need to think through the options... |
Hi @bluepnume , I've just submitted PR #335 on an attempt to improve this. On my project I've created a local version of the react driver to keep moving, so I've kind of tested it, and seems to be working as expected now: no uncaught errors after component close() or destroy(). |
@tmilar Looks like you have a flow error. Any update on when you may have tests for this? |
any update on this issue? @bluepnume i see this error every time i click the back button before the iframe finishes loading |
Any update on this? |
February 2024, Any update on this? |
still having issues with this.... any update? |
On March, are there any update? |
We're also experiencing this issue over here and could use this fix. |
Hi, I've running into this trouble and I couldn't sort it out.
Scenario
zoidComponent.close()
to clean the zoid frame.EVENTS.CLOSED
(or,EVENTS.DESTROYED
) event - or a way to await for the component to properly be disposed, or a way to handle/catch the error.Context
Below is how the user (parent site) is triggering the "close" action of the iframe which is rendered on the modal. Then, it listens for the zoid
CLOSE
/DESTROY
events, and after that it executes a callback so the container modal can be effectively removed from the view.This is how the parent app is instantiating the component:
And this is how the parent site renders it:
What I've tried
try {} catch {}
block arround theclose()
method, and also a promise.catch()
. None have worked.<Zoid/>
react component with an error boundary. Didn't work.**What I'd expect **
close()
call should not result in an unhandled error, or.catch()
ortry {} catch {}
in some place, or....close()
/EVENTS.CLOSE
action to be fully completed to let me safely remove the container component from the page.The text was updated successfully, but these errors were encountered: