react-popper incorrect position on mount

Viewed 6212

I have built a custom tree view in React, and each item contains a dropdown which is positioned using Popper. Since the child elements are not visible on render, Popper is not positioning the dropdown correctly, for example:

enter image description here

When the tree is open on mount (i.e the children are visible), the positioning is correct:

enter image description here

Each level in the tree is rendered via a CategoryNavItem component, which essentially looks like this:

<div className={ className.join(' ') }>
    <div className={ `collection-nav_item-link depth${depth}` } style={{ paddingLeft: `${paddingLeft}px`}}>
        <Link to={ linkTo } onClick={() => { setIsOpen(!isOpen) }}>
            <i className="collection-nav_item-link_icon"></i>
            <span className="collection-nav_item-link_text">{ category.name }</span>
        </Link>

        <Dropdown
            toggleClassName="btn-icon-white btn-sm"
            toggleContent={ <Icon name="ellipsis-h" />}
            position="bottom-end"
            size="small"
            items={[ 
                { text: 'Edit category' },
                { text: 'Add subcategory', onClick: (() => { dispatch(openAddSubcategory(category)) }) }
            ]} />
    </div>
    { children }
</div>

The Dropdown component is where we use Popper, and it works well everywhere else. The visibility of a CategoryNavItem is handled via the component's state in React.

Is there any way to trigger Popper's update() method programmatically in React? We should force update when toggling the item's visibility.

2 Answers

It turns out we just need to expose the update property from the usePopper hook, and then call it when setting the dropdown's visibility, for example:

const { styles, attributes, update } = usePopper(referenceElement, popperElement, {
    placement: placement,
    modifiers: [
        { name: 'arrow', options: { element: arrowElement } },
        { name: 'offset', options: { offset: [ 0, 3 ] } }
    ]
});

And similarly:

const toggleDropdown = (e) => {
    e.preventDefault();
    e.stopPropagation();

    setVisible(!visible);
    update();
};

According to the docs, you can manually update the propper instance so that it recomputes the tooltip position:

Manual update

You can ask Popper to recompute your tooltip's position by running instance.update().

This method will return a promise, that will be resolved with the updated State, from where you will optionally be able to read the updated positions.

const state = await popperInstance.update();

When clicking on your item visibility toggle, you could add your popper manual update, like the line of code above.

Here is the reference.

Related