emilkowalski / vaul

An unstyled drawer component for React.
https://vaul.emilkowal.ski
MIT License
5.26k stars 169 forks source link

Changing Modal Prop to true or false conditionally causes drawer to rerender content #372

Open thediveshsharma opened 3 weeks ago

thediveshsharma commented 3 weeks ago

modal={isMenuOpen ? false : true}

When doing something like this, the drawer content re-renders and whatever typed in input just disappears. <DrawerRoot className="xyz" open={open} onOpenChange={onOpenChange} modal={isMenuOpen ? false : true} // onClose={onClose}

{children} ----> it has a component with input whatever typed in it will disappear as soon as modal prop changes.

thediveshsharma commented 3 weeks ago

https://codesandbox.io/p/devbox/drawer-non-dismissable-forked-xkwd25?file=%2Fapp%2Fmy-drawer.tsx%3A64%2C34&workspaceId=e86b3ebb-ab1b-4e36-9117-eb96c9581766

Type anything in the input in code sandbox.. wait for modal prop to change, as soon as modal prop changes text disappears since drawer re-renders

thediveshsharma commented 3 weeks ago

Can someone help with this?

thediveshsharma commented 3 weeks ago

any update on this?

HashCookie commented 1 week ago

Hi, have you solved it? I also have the same issue.

liamb13 commented 1 week ago

I believe I've found a work around.

It's a little bit hacky, but it worked for me.

Under the hood, it's using the same functionality as radix-ui (thanks to this reddit thread).

Rather than change the Drawer itself, here's my approach.

'use client';
import { ReactNode, useEffect, useState } from 'react';

import { classNames } from '~/utils/classNames';

import { RemoveScroll } from 'react-remove-scroll';

import { Drawer, DrawerContent } from '~/components/ui/drawer';

const snaps = {
  mini: '220px',
  expand: 0.75,
};

export default function MobileDrawer({ children }: { children: ReactNode }) {
  const [snap, setSnap] = useState<number | string | null>(snaps.mini);
  const [isOpen, setIsOpen] = useState(false);

  useEffect(() => {
    if (snap === snaps.expand) {
      setIsOpen(true);
    } else {
      setIsOpen(false);
    }
    return () => {
      setIsOpen(false);
    };
  }, [snap]);
  return (
    <>
      <RemoveScroll enabled={isOpen}>
        <div className="pointer-events-none fixed inset-0" />
      </RemoveScroll>
      <Drawer
        shouldScaleBackground={false}
        dismissible={false}
        modal={false}
        activeSnapPoint={snap}
        setActiveSnapPoint={setSnap}
        open={true}
        snapPoints={[snaps.mini, snaps.expand]}
      >
        <DrawerContent className="pointer-events-auto fixed bottom-0 left-0 right-0 h-full flex-col rounded-t-[10px] bg-white">
          <div
            className={classNames(
              {
                'overflow-y-scroll': snap !== snaps.mini,
                'overflow-hidden': snap === snaps.mini,
              },
              'max-h-[calc(80vh - 24px)]',
            )}
          >
            {children}
          </div>
        </DrawerContent>
      </Drawer>
    </>
  );
}

Then in your stylesheet:

body[data-scroll-locked] {
  pointer-events: none;
}

Please let me know if this works for you.