skip to Main Content

I am coding a FlyoutManager from which I can toggle every flyout menus in react app. I am trying to set the property to close when clicking outside. I found on another stackoverflow question that my clicking-outside function is triggered when I click the button to close it so it re-opens the flyout menu because it toggles the menu state twice.

Have you got some ideas to fix my problem ?

Here is the Header code simplified to my problem :

export const Header = () => {
    const {toggleFlyout} = useFlyout();
    return (
        <header>
            <div>
                /*
                    ...
                    Other components
                */
                <nav>
                    /*
                        ...
                        Other buttons
                    */
                    <button id="UserFlyout" onClick={() => toggleFlyout("UserFlyout")}>
                        User-logo
                    </button>
                </nav>
            </div>
        </header>
    );
};

Here is the FlyoutContext Folder which contains the toggle function :

export const FlyoutContext = createContext();

export const FlyoutProvider = ({children}) => {
    const [flyout, setFlyout] = useState(null);
    const [isOpen, setIsOpen] = useState(false)

    const toggleFlyout = name => {
        setIsOpen(!isOpen);
        if (!isOpen) {
            setFlyout(name);
        } else {
            setFlyout(null);
        }
    }

    return (
        <FlyoutContext.Provider value={{flyout, toggleFlyout}}>
            {children}
        </FlyoutContext.Provider>
    );
};

export const useFlyout = () => useContext(FlyoutContext);

Here is the FlyoutManager Folder

const FlyoutList = {
    UserFlyout: UserFlyout
};

export const FlyoutManager = () => {
    const {flyout, toggleFlyout} = useFlyout();
    
    const flyoutRef = useRef(null);

    const Flyout = FlyoutList[flyout];

    useEffect(() => {
        const handler = event => {
            if (flyoutRef.current !== null && !flyoutRef.current.contains(event.target)) {
                toggleFlyout(flyout);
            }
        };

        document.addEventListener('mousedown', handler);
        return () => {
            document.removeEventListener('mousedown', handler);
        };
    }, [flyoutRef, flyout, toggleFlyout]);  

    if (!flyout) return null;

    return (
        <Flyout flyoutRef={flyoutRef}>
            <div ref={flyoutRef}>
                user flyout
            </div>
        </Flyout>
    );
};

Thanks a lot for your help

Some ideas might be to check if the mousedown event target is not the button. But because I use a context I can’t access the id button in the FlyoutContext.

2

Answers


  1. Chosen as BEST ANSWER

    Would it be crappy if I keep the open flag to use is as a condition in the button callback function like so :

    <button onClick={() => {isOpen ? closeFlyout() : openFlyout("UserFlyout")}}><FaRegUser/></button>
    
    

    In fact, dividing the toggleFlyout function doesn't change anything regarding the double toggling when I click the header button to close the flyout.


  2. You’ve got two options, one is quite diffucult while the other is really simple:

    • either you somehow put the mousedown handler on something that covers the entire thing, including the button,
    • or you can just split the toggleFlyout into two idempotent functions.

    You can also entriely skip the open flag since it always comes together with the name, so if there is no name it means no flyout is open:

    function openFlyout(name) {
      setFlyout(name);
    }
    
    function closeFlyout() {
      setFlyout(null);
    }
    
    return (
      <FlyoutContext.Provider value={{flyout, openFlyout, closeFlyout}}>
        {children}
      </FlyoutContext.Provider>
    );
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search