react-native-dropdown-picker
react-native-dropdown-picker copied to clipboard
support auto dimiss when tap out of the dropdown panel
Hi, Assume we have the multiple dropdown aligned in the vertical one by one. the multi dropdowns can be opened at the same time. If we provide a auto dimiss feature when user tap out of dropdown panel, that will be great.
<DropDownPicker/>
<DropDownPicker/>
<DropDownPicker/>
<DropDownPicker/>
currently, we use a global hook to auto dismiss the opened dropdown to bypass this issue.
Hy @kongshu612, i think you are missing some props there.
in order to use multiple dropdown, you need to pass the open props on the component and also you need to maintain the state for all the related dropdown picker accordingly.
So in your case, it should be like
const [open1,setopen1] = useState(false);
const [open2,setOpen2] = useState(false);
const [open3,setOpen3] = useState(false);
<DropDownPicker
multiple={true}
listMode="MODAL"
open={open1} // this one
value={value1}
items={items1}
setOpen={setOpen1}
/>
<DropDownPicker
multiple={true}
listMode="MODAL"
open={open1} // this one
value={value2}
items={items2}
setOpen={setOpen2}
/>
So in your case, you have 4 pickers, you need to maintain the state for 4 different pickers just like above. I hope i am able to answer your issue.Correct me if i failed to understand your issue.
hi @aashishshrestha5532 , thanks for your reply. my case was a little different with what you comment. it make sense to maintain isolated open state for each dropdown in the component level. In my case, these dropdowns live in different components, and those components are composed in one screen. Currently, my workaround is to maintain such open state in the global level, which I think is not good.
If we provide a transparent mask behind the dropdown panel and tap the mask will auto dismiss the dropdown panel, that will be great.
Hi, I have the same problem, did you manage to solve it?
I'm facing the same issue, any workaround?
Hi @MatheusHCP , @durgesh94 ,sorry for reply late. My workaround is to define a global state by using hookAPI. see the code bellow
import React from 'react';
export interface IClose {
close: () => void;
displayName?: string;
}
let modals: Set<IClose> = new Set();
let activeModal: IClose | undefined;
function push(instance: IClose) {
if (!modals.has(instance)) {
modals.add(instance);
}
}
function popup(instance: IClose) {
if (modals.has(instance)) {
modals.delete(instance);
}
if (activeModal === instance) {
activeModal = undefined;
}
}
function toggleOpen(instance: IClose, open: boolean) {
if (open) {
if (activeModal !== instance) {
activeModal?.close();
activeModal = instance;
}
} else {
if (activeModal === instance) {
activeModal = undefined;
}
}
}
export function useAutoClose(instance: IClose) {
React.useEffect(() => {
push(instance);
return () => {
popup(instance);
}
}, [instance]);
return React.useCallback((open: boolean) => {
toggleOpen(instance, open);
}, [instance]);
}
Hi @MatheusHCP , @durgesh94 ,sorry for reply late. My workaround is to define a global state by using hookAPI. see the code bellow
import React from 'react'; export interface IClose { close: () => void; displayName?: string; } let modals: Set<IClose> = new Set(); let activeModal: IClose | undefined; function push(instance: IClose) { if (!modals.has(instance)) { modals.add(instance); } } function popup(instance: IClose) { if (modals.has(instance)) { modals.delete(instance); } if (activeModal === instance) { activeModal = undefined; } } function toggleOpen(instance: IClose, open: boolean) { if (open) { if (activeModal !== instance) { activeModal?.close(); activeModal = instance; } } else { if (activeModal === instance) { activeModal = undefined; } } } export function useAutoClose(instance: IClose) { React.useEffect(() => { push(instance); return () => { popup(instance); } }, [instance]); return React.useCallback((open: boolean) => { toggleOpen(instance, open); }, [instance]); }
i'm not sure why you have a modals set and all those extra functions but this worked for me:
import React, { SetStateAction } from "react";
export interface IClose {
setOpen: (value: boolean) => void;
id: string;
}
let activeDropdown: IClose | undefined;
function toggleOpen(instance: IClose, open: SetStateAction<boolean>) {
if (open) {
if (activeDropdown !== instance) {
activeDropdown?.setOpen(false);
activeDropdown = instance;
}
instance.setOpen(true);
} else {
if (activeDropdown === instance) {
activeDropdown = undefined;
}
instance?.setOpen(false);
}
}
export function closeActiveDropdown() {
activeDropdown?.setOpen(false);
activeDropdown = undefined;
}
export function useAutoCloseDropdown(instance: IClose) {
return React.useCallback(
(open: SetStateAction<boolean>) => {
toggleOpen(instance, open);
},
[instance]
);
}
In the component where I render the dropdown:
const [open, setOpen] = useState(false);
const toggle = useAutoCloseDropdown({ id: label, setOpen });
Then simply pass toggle to the setOpen prop of DropdownPicker
For future coders with the same problem: you can also wrap your page in a <TouchableWithoutFeedback> and set the onPress to the globally exported closeActiveDropdown so that tapping outside of the dropdown closes it.
And if you want to use this with a ScrollView inside, it's gonna disable the scrolling, so add an enclosing View inside your ScrollView and add this prop to it: onStartShouldSetResponder={() => true} (from StackoverFlow)