react-popper incorrect position on mount
Asked Answered
A

2

9

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.

Appraisal answered 5/1, 2021 at 20:8 Comment(0)
A
18

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();
};
Appraisal answered 6/1, 2021 at 19:13 Comment(2)
this helped me alot; it wasn't very clear on the hooks documentation of the library on how to use update. thank youMayence
The update function is async so you will need to use await update() in an async funcDeclamatory
D
3

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.

Discoid answered 6/1, 2021 at 9:19 Comment(1)
Yes, this works for usual JS implementation, but we do not have access to the popperInstance in React, since it is handled via the Popper provider.Appraisal

© 2022 - 2024 — McMap. All rights reserved.