everweij / react-laag

Hooks to build things like tooltips, dropdown menu's and popovers in React

Home Page:https://www.react-laag.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Controlled + TriggerRef from outside

anilanar opened this issue · comments

Our codebase @userlike has a very similar menu solution, so we had plans to reuse react-laag and potentially contribute with bug fixes.

Now I gave it a try to replace our menu/dropdown/tooltips with react-laag, but sadly it didn't work out.

Our "Dropdown" component takes the triggerRef from outside (because we have a few cases in which triggerRef is needed by others) and it can be both controlled and uncontrolled.

useToggleLayer looks pretty good for our use cases, but it lacks a controlled mode.

Our Dropdown component has the following prop:

interface DropdownProps {
  openState?: [boolean, React.Dispatch<React.SetStateAction<boolean>>];
}

It's optional. If it's not passed, Dropdown is uncontrolled. Otherwise it's controlled.

Actually, let me give the full picture:

  const openState = useState(false);
  const [open, setOpen] = openState;

  const triggerRef = useRef();
  const layerRef = useRef();
  
  return (
      <Dropdown layer={() => <div ref={layerRef}>Hello</div>} layerRef={layerRef} triggerRef={triggerRef} openState={openState} ...>
        <button ref={triggerRef} onClick={() => setOpen(open => !open)}>Toggle</button>
      </Dropdown>
  );

Dropdown manages positioning the layer, closing on outside clicks etc.
And we have another component that reuses Dropdown to attach key binds and hovers to make an accessible dropdown menu. We've noticed that Dropdown has the best chance of reuse if layerRef, triggerRef and open state can be passed from outside.

Dropdown's behaviour is encoded inside a useDropdown hook, so other hooks can reuse dropdown's logic.

Sorry for my lengthy/unstructured writing.

I'm having the same problem than you. I did solve this issues with the npm package react-merge-refs But I use ToggleLayer instead of useToggleLayer.

targetRef = useRef()
({ triggerRef, toggle, close, isOpen }) => (
<div ref={mergeRefs([triggerRef, targetRef])>{children}</div>
)

It's an hack solution but it should have work fine for your need.

I'm trying to push the feature even further by removing div need to hold the triggerRef sending it to the child. MergeRefs isn't doing well in this scenario, so I will try to make few edit and allow ref to be given from outside. I think it's too late for you now, but I might reference this issue later on.