Search code examples
javascriptaccessibilitydownshift

How to acquire actual event triggered from downshift useSelect item change


I'm using the useSelect hook provided by downshift.

I have a situation where I'm unable to change the items passed into useSelect.

Each menuItem has a handler that expects the event as a parameter, but I don't see a way to get access to the actual event triggered by the user.

A typical menu item looks like this

menuItems: [{
  label: 'foo',
  value: 'bar',
  onClick: (event) => doTheThing(event),
}]

In previous cases I've leveraged the onSelectItemChange handler to obtain the selected item

const { getItemProps, getLabelProps, getMenuProps, getToggleButtonProps, highlightedIndex, isOpen } = useSelect({
  items: menuItems,
  onSelectedItemChange: ({ selectedItem }) => {
    // call the item's event handler
    selectedItem.onClick(<here's where I need the actual event object>);
  }
});

but the arguments passed to onSelectedItemChange do not include the actual event that triggered the selected item change.

I've searched through the downshift repo for a function that returns the event, but nothing stands out.

Perhaps there's another strategy I can use to acquire the event?


Solution

  • This confused me as well, but after some digging around in the github documentation I found that that there are a few supported ways to expose the event object to either conditionally alter event behavior or override it entirely. These examples helped clarify the different levels of control you can exhibit:

    1. Pass a custom handler for the targeted event to the prop getter, per the docs your custom handler will be called before the downshift handler

      const items = [...] // items here.
      const {getMenuProps} = useSelect({items})
      const ui = (
      /* button, label, ... */
      <ul
         {...getMenuProps({
              onKeyDown: event => {
              // your custom keyDown handler here.
            },
         })}
      />)
      
    2. Bypass the default downshift behavior entirely and call the handler outside of the prop getter

       const items = [...] // items here.
       const {getMenuProps} = useSelect({items})
       const ui = (
         /* button, label, ... */
         <ul
           {...getMenuProps()}
           onKeyDown={event => {
             // your custom keyDown handler here.
           }}
         />
       )
      

    It's worth noting that this will expose the wrapped React synthetic event here and you will need to reach down one more level to get to the native event object itself.

    There is also a code sandbox with some examples: https://codesandbox.io/s/l2xqz1qyll?file=/index.js