Trigger openPortal or any action without event
Sebastp opened this issue ยท 19 comments
I'm trying to openPortal on route change.
When route changes, I get;
You must either add a ref
to the element you are interacting with or pass an event
to openPortal(e) or togglePortal(e).
How do I do it without ref or event?
This is a good point. The way you are trying to use the library I haven't seen before. Every way that I've thought to use it was by interacting with something. i.e. click a button, have a modal show, hover an element, have a tooltip show. As a workaround until I get this changed, you could add a ref to a random element.
const App = () => {
const { ref, Portal } = usePortal()
return (
<>
<div style={{ display: 'none' }} ref={ref} />
<Portal />
</>
)
}
That should work until I get this changed
Nope, it does not.
Still getting the same error
I will take a look at this asap. Swamped with work right now :/
Still getting "Error: You must either add a ref
to the element you are interacting with or pass an event
to openPortal(e) or togglePortal(e)."
Does anyone have a workaround?
I have a similar use case where a modal can be opened programmatically without user interaction when an API returns an error response. It would be great to support this when you get a chance @alex-cory.
In the meantime @Sebastp I am using something like this as a workaround:
import React, { useEffect } from 'react';
import usePortal from 'react-useportal';
// Passing this to openPortal tricks it into thinking it was from a user
const NULL_EVENT = { currentTarget: true };
const Modal = () => {
const { openPortal, closePortal, isOpen, Portal } = usePortal();
useEffect(() => {
openPortal(NULL_EVENT);
}, []);
return (
isOpen && (
<Portal>
<div>
This will open immediately
<button onClick={closePortal}>close</button>
</div>
</Portal>
)
);
};
I have a similar use case where a modal can be opened programmatically without user interaction when an API returns an error response. It would be great to support this when you get a chance @alex-cory.
In the meantime @Sebastp I am using something like this as a workaround:
import React, { useEffect } from 'react'; import usePortal from 'react-useportal'; // Passing this to openPortal tricks it into thinking it was from a user const NULL_EVENT = { currentTarget: true }; const Modal = () => { const { openPortal, closePortal, isOpen, Portal } = usePortal(); useEffect(() => { openPortal(NULL_EVENT); }, []); return ( isOpen && ( <Portal> <div> This will open immediately <button onClick={closePortal}>close</button> </div> </Portal> ) ); };
Thanks man <3 I'll try it out
+1
import React, { useEffect } from 'react';
import usePortal from 'react-useportal';
// Passing this to openPortal tricks it into thinking it was from a user
const NULL_EVENT = { currentTarget: true };
const Modal = () => {
const { openPortal, closePortal, isOpen, Portal } = usePortal();
useEffect(() => {
openPortal(NULL_EVENT);
}, []);
return (
isOpen && (
<Portal>
<div>
This will open immediately
<button onClick={closePortal}>close</button>
</div>
</Portal>
)
);
};
This works in an SSR scenario but then there seems to be issues when i try and interact with any element inside the portal (which is a modal in my implementation) as i get the following error:
Uncaught TypeError: target.current.contains is not a function
at containsTarget (usePortal.js:114)
at usePortal.js:115
at HTMLDocument.<anonymous> (usePortal.js:126)
which points to :
.......
var handleOutsideMouseClick = react_1.useCallback(function (e) {
var containsTarget = function (target) { return target.current.contains(e.target); };
......
Since interacting with the modal is crucial in my app currently, directly editing the node_modules
dist file with var containsTarget = function (target) { return target.current && target.current.contains(e.target) || null; };
seems to make it work alright.
I guess its not recommended at all but it is allowing me to continue on with the rest of the app development for now. Not sure if this fix could effect other functionalities and use-cases of this package though it does seem a rather innocuous change.
I too have cases where I have to open modals programatically w/o any buttons and I'm using all kinds of nasty workarounds. Any input on this issue is greatly appreciated.
If the NULL_EVENT
workaround suggested by @talbet-qga actually works, is there ever any need to pass an actual event or ref
? Or is there some situation where it would still fail?
I have my own useModal()
wrapper around usePortal()
and I was thinking if I should just change it to always use a NULL_EVENT
. I've been using empty <div ref={ref} />
elements in my components only for opening modals which is really clumsy.
NULL_EVENT
does not work in my experience. I've also been using empty divs and what not, but definitely not optimal.
I only have experience of one case, where a modal that was set up in a custom hook was supposed to be opened in a parent component's useEffect()
hook when certain conditions applied. In that case, I wasn't able to get it working with ref
, and there obviously was no user-initiated event, but the NULL_EVENT
trick worked.
It turns out NULL_EVENT
isn't foolproof, but to avoid Uncaught TypeError: target.current.contains is not a function
you could try this:
const NULL_EVENT = { currentTarget: { contains: () => false } };
@alex-cory have you had a chance to look at this? Would you accept a PR?
Apologies, I have not had a chance to take a look. Yes, I would absolutely accept a PR!
+1