FezVrasta/popper.js

View on GitHub
website/pages/docs/useInteractions.mdx

Summary

Maintainability
Test Coverage
<PageCard>

# useInteractions

A hook to merge or compose interaction event handlers together,
preserving memoization.

```js
import {useInteractions} from '@floating-ui/react';
```

<ShowFor packages={['react-dom']}>

<PackageLimited>@floating-ui/react only</PackageLimited>

</ShowFor>

</PageCard>

Interaction Hooks like `useHover(){:js}` and `useFocus(){:js}` do
two things: they create Effects inside themselves that work
independently, and also return event handlers intended to be
passed to the elements to add their functionality.

## Usage

`useInteractions(){:js}` accepts an array of the values returned
from interaction Hooks, merging their event handlers into prop
getters used for rendering:

```js /hover/ /focus/
import {
  useFloating,
  useHover,
  useFocus,
  useInteractions,
} from '@floating-ui/react';

function App() {
  const {context} = useFloating();

  const hover = useHover(context);
  const focus = useFocus(context);

  const {getReferenceProps, getFloatingProps} = useInteractions([
    hover,
    focus,
  ]);
}
```

<Notice title="Conditional interactions">
  
Don't conditionally pass values into the interactions array if you don't want 
one of the interactions to be enabled. This is because each Hook also has 
Effects that register independent event handlers globally, among other side 
effects.

Instead, use the `enabled{:.key}` option that each Hook has -- it
simultaneously disables all Effects _and_ event handler props so
the Hook is fully inert.

</Notice>

## External reference

In your component API, the reference element may be external to
the component that `useFloating(){:js}` is called in (where the
positioning data is passed). In such a tree structure, the
interactions are shared between the reference element and
floating element in a "root" component higher than them -- an
ancestor common to both.

An example would be:

{/* prettier-ignore */}
```js
<TooltipRoot> {/* useInteractions() called in this component */}
  <TooltipTrigger />
  <TooltipPopup /> {/* useFloating() called in this component */}
</TooltipRoot>
```

To share the interactions between the reference and floating
elements, you can use the `useFloatingRootContext(){:js}` Hook:

```js
import {useFloatingRootContext} from '@floating-ui/react';
```

It returns a <WordHighlight id="a">context object</WordHighlight>
that is accepted by all interaction Hooks, similar to the one
returned by `useFloating(){:js}` -- only without the positioning
data.

Pass the open state and elements to the Hook:

```js {8-15,28} /context/#a
function TooltipRoot() {
  const [isOpen, setIsOpen] = useState(false);

  const [anchor, setAnchor] = useState(null);
  const [tooltip, setTooltip] = useState(null);

  const context = useFloatingRootContext({
    open: isOpen,
    onOpenChange: setIsOpen,
    // Required: both elements must be passed externally.
    // Store them in state.
    elements: {
      reference: anchor,
      floating: tooltip,
    },
  });

  const click = useClick(context);

  const {getReferenceProps, getFloatingProps} = useInteractions([
    click,
  ]);

  return (
    <>
      <Anchor setAnchor={setAnchor} {...getReferenceProps()} />
      <Tooltip
        rootContext={context}
        setTooltip={setTooltip}
        {...getFloatingProps()}
      />;
    </>
  );
}
```

The root context must be available to `useFloating(){:js}` by
passing it as the `rootContext` option:

```js {3}
function Tooltip({rootContext, setTooltip, ...props}) {
  const {floatingStyles} = useFloating({
    rootContext,
  });
  return <div ref={setTooltip} {...props} />;
}
```

## Return value

```ts
interface UseInteractionsReturn {
  getReferenceProps(
    userProps?: React.HTMLProps<Element>,
  ): Record<string, unknown>;
  getFloatingProps(
    userProps?: React.HTMLProps<HTMLElement>,
  ): Record<string, unknown>;
  getItemProps(
    userProps?: React.HTMLProps<HTMLElement>,
  ): Record<string, unknown>;
}
```

The Hook returns two core prop getters, one for the reference
element and one for the floating element. These prop getters
should be spread onto the elements:

```js
<>
  <div ref={refs.setReference} {...getReferenceProps()} />
  <div
    ref={refs.setFloating}
    style={floatingStyles}
    {...getFloatingProps()}
  />
</>
```

All event handlers you pass in should be done so through the prop
getter, not the element itself:

```js
<div
  ref={refs.setReference}
  {...getReferenceProps({
    onClick: () => console.log('clicked'),
    onFocus: () => console.log('focused'),
  })}
/>
```

This is because your handler may be either overwritten or
overwrite one of the Hooks' handlers. More event handlers may
also be added in future versions.

## `getItemProps{:.const}`

A third prop getter is returned for item elements when dealing
with a list inside the floating element, which is not required
for all types of floating elements. See
[`useRole`](/docs/useRole#role) for more information on this prop
getter for listbox (e.g. select or combobox) or menu roles.

{/* prettier-ignore */}
```js /getItemProps/
const {
  getReferenceProps, 
  getFloatingProps, 
  getItemProps
} = useInteractions([]);
```