React中Flowbite Drawer组件left/bottom位置异常问题求助
Fix for WrapperDrawer Left/Bottom Placement Issues
Key Issues Identified
- Initial Visibility: The drawer wasn't hidden by default, leading to it being visible on load for left/bottom placement (since their default fixed positioning puts them in the viewport).
- Missing Placement Positioning: No explicit
left-0/bottom-0classes were added, causing Flowbite's Drawer to apply incorrect slide animations and positioning. - Race Condition & Instance Management: Using
useStatefor the Drawer instance caused unnecessary re-renders, and the instance wasn't reinitialized when placement changed.
Corrected Code
type CustomDrawerPropsType = { id: string; children: React.ReactNode; isOpen: boolean; setOpen?: (val: boolean) => void; showCloseButton: boolean; }; const WrapperDrawer = (props: CustomDrawerPropsType & DrawerOptions) => { const targetEl = React.useRef<HTMLDivElement>(null); const drawer = React.useRef<DrawerInterface | null>(null); const DEFAULT_BACKDROP_STYLE = 'bg-gray-700/50 dark:bg-gray-900/80 fixed inset-0 z-30'; const options: DrawerOptions = { placement: props.placement ?? 'right', backdrop: props.backdrop ?? true, bodyScrolling: props.bodyScrolling ?? false, edge: props.edge ?? false, edgeOffset: props.edgeOffset ?? '', backdropClasses: props.backdropClasses ?? DEFAULT_BACKDROP_STYLE, onShow: props.onShow ?? (() => {}), onToggle: props.onToggle ?? (() => {}), onHide: () => { props.onHide?.(); props.setOpen?.(false); // Sync parent state when drawer is hidden }, }; const instanceOptions: InstanceOptions = { id: props.id, override: true, }; // Initialize/reinitialize Drawer on mount or placement change React.useEffect(() => { if (targetEl.current) { // Cleanup existing instance if any if (drawer.current) { drawer.current.destroy(); } drawer.current = new Drawer(targetEl.current, options, instanceOptions); } // Cleanup on unmount return () => { drawer.current?.destroy(); }; }, [props.placement]); // Sync drawer visibility with isOpen prop React.useEffect(() => { if (drawer.current) { props.isOpen ? drawer.current.show() : drawer.current.hide(); } }, [props.isOpen]); // Get placement-specific position and size classes const getPlacementClasses = (): string => { switch (props.placement) { case 'top': return 'top-0 w-screen h-3/5'; case 'bottom': return 'bottom-0 w-screen h-3/5'; case 'left': return 'left-0 h-screen w-3/5'; case 'right': default: return 'right-0 h-screen w-3/5'; } }; return ( <div id={props.id} ref={targetEl} className={`fixed z-40 overflow-y-auto bg-white dark:bg-gray-800 hidden ${getPlacementClasses()}`} tabIndex={-1} aria-labelledby={`${props.id}-label`} > {/* Accessibility label */} <h5 id={`${props.id}-label`} className="sr-only">Drawer Menu</h5> {props.showCloseButton && ( <button type="button" aria-controls={props.id} className="absolute right-2.5 top-2.5 inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-white" onClick={() => { drawer.current?.hide(); }} > <svg className="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14" > <path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" /> </svg> <span className="sr-only">Close menu</span> </button> )} {props.children} </div> ); };
Key Changes Explained
- Initial Hidden Class: Added
hiddento the drawer's className to ensure it's hidden on first render, preventing the initial visibility bug. - Placement-Specific Position Classes: The
getPlacementClassesfunction now addsleft-0/bottom-0/right-0/top-0to properly position the drawer based on placement, which Flowbite uses to apply correct slide animations. - Drawer Instance as Ref: Switched from
useStatetouseReffor the Drawer instance to avoid unnecessary re-renders and manage the instance more efficiently. - Reinitialize on Placement Change: Added
props.placementto the useEffect dependency array, so the Drawer is destroyed and recreated when placement changes, ensuring correct behavior. - State Sync: Updated the
onHideoption to callprops.setOpen(false)to sync the parent component's state with the drawer's visibility, and updated the close button to triggerhide()(which now syncs state). - Accessibility Fix: Added a unique accessibility label tied to the drawer's ID for better screen reader support.
内容的提问来源于stack exchange,提问作者pra-bhu
相关产品推荐
相关产品推荐

