Feature request: listener onOpen/onClose
Closed this issue ยท 15 comments
In some cases you want to handle open/closed state management from a parent component (for instance, if you want to close the modal after some data has been saved). I can open/close the portal via a prop by doing something like this in the component:
const { openPortal, closePortal, isOpen, Portal } = usePortal({ isOpen: props.open })
useEffect( () => {
if ( props.open === false && isOpen ) closePortal()
if ( props.open && !isOpen ) openPortal()
}, [ props.open, isOpen ] )
And it would be nice to also have an onOpen/onClose to respond to closing the portal via ESC or clicking outside the modal.
Hope it's clear. Thanks!
oh interesting! I'll get to work asap on these :) And can you elaborate a little with an example for the onOpen/onClose
Sweet. The example is a modal where the state is fully managed from parent. Would a CodeSandbox help to clarify?
Here's an example of a managed modal, I tried to explain the use case as well: https://codesandbox.io/s/useportal-bu7j0
As you can see, closing it after saving works fine, but because there are no open/close events I cannot tap in to the built-in functionality of closing on ESC/clicking outside the modal.
Awesome, I will take a look at this tomorrow! :) Sorry for the delay, a lot going on at work currently
Okay, so after looking at this again with a clear head, what's stopping you from using the "stateless" Portal here? https://codesandbox.io/s/useportal-managed-state-j5m1x
I'm trying to see if what you're asking works for the onClose/onOpen/isOpen. Still not done with solution, but will keep going after work
Okay, so I don't think this can actually be done the way you want. I might be able to get it so there could be something like
const { onPortalClose, onEsc, onOutsideClick } = usePortal()
onPortalClose(() => {
// run whatever is passed in
})
but I haven't figured out if that's^ possible yet.
The problem is when you do it this way
const { closePortal } = usePortal({ onClose, isOpen })
is that, unlike components, hooks don't "rerender with updated arguments" like components do with props. Does this make sense?
Hi Alex, sorry for the late reply, was hiking in the Dolomites ๐
The managed state example at https://codesandbox.io/s/useportal-managed-state-j5m1x doesn't work for the same reason as mine: the built-in ESC and outside click functionality is missing.
What do you think about this API?
<Portal onOpen={...} onClose={...}>
...
</Portal>
Would that solve the problem?
Hmm... so I thought about this previously, but how would you get the props inside the <Portal />
from inside the usePortal
hook. I need to think about this some more
At the risk of it being not relevant, maybe this hook can be of inspiration? https://github.com/wsmd/react-use-form-state/blob/master/src/useFormState.js
Another idea that could maybe inspire, here's an approach I used for one of my functional components to be able to control open/closed state from the outside. Use case is a popover menu; the component also contains the trigger and so normally it controls its own state, but in some cases we want to keep it open on clicks, and close it manually โ for example a date picker where you don't want to close it on month navigation, but do want to close after a user has selected a date and the change was saved:
In the code implementing it I can do <Menu ref={ref => this.menu = ref}>
, and then this:
handleDate( date ) {
this.props.onDate( date )
this.menu.close()
}
No idea if this is a valid use of React refs... ๐ but it works.
if this is a valid use of React
No, to create something like on your shcreenshot you need useImperativeHandle
:
React Docs
@infodusha Thanks for the pointer!
I'm going to play around with the onClose
and onOpen
soon. I just haven't had time yet. I'm sure there's a way to implement it