nanopop
nanopop copied to clipboard
Usage with React
Hey there. Is it safe to use with react lifecycle hooks? As I noticed nanopop has no method to destroy an instance and react will try to create another one every re-render. Thanks
here is example snippet
React.useLayoutEffect(() => {
if (!isOpened) {
return
}
const _reference = reference.current
const _popper = popper.current
let nanopop: NanoPop
if (_reference && _popper) {
nanopop = new NanoPop(_reference, _popper, {
margin: 4,
position: 'bottom-end',
})
nanopop.update()
}
// expected behavior
return () => nanopop.destroy()
}, [isOpened])
Hi :) NanoPop does not need to be destroyed manually as it does not bind any events to an elemenet. The only reason for re-creating a class is that it's convenient to just call .update() without re-providing any options. If a nanopop instance falls out of scope it'll get garbage collected automatically, no need to do any cleanup manually.
Maybe it was a bad design decision to use a class, I'll consider releasing a second version with just a function called usePopper or something like that to make it look less stateful (considering that using new is quite expensive).
Maybe it was a bad design decision to use a class, I'll consider releasing a second version with just a function called
usePopperor something like that to make it look less stateful (considering that usingnewis quite expensive).
I would be very grateful to you!
it does not bind any events to an elemenet
That lib becomes even better for me lol. Thanks, I think you can close this issue. Cheers
it does not bind any events to an elemenet
That lib becomes even better for me lol. Thanks, I think you can close this issue. Cheers
Can you show me your example of using this plugin? I want to create my own dropdown and use this plugin as a dropdown content positioning tool. I have a problem that the reference element is missing in the dom, so I cannot initialize the plugin. The workaround that I use is to wait for the time when the content is first mounted in the dom (I use portals), at which point I initialize nanoPop for the first time) Thanks
Version 2 is in progress... I hope this will only take a few days and I'll ping you @7iomka as soon as it's "finish" since there are major API changes :)
@mod3x I'll keep this open as long as the second version is in progress, you made me realize that the current API doesn't make that much sense as it's a really low level library to position things. At first I tried to imitate Popper but this is definitely going in a completely separate direction! 😄
Okay I've managed to create a minimal demo on codesandbox. Make sure to checkout the latest README :)
Great work! I'll update to the v2 :)
Okay I've managed to create a minimal demo on codesandbox. Make sure to checkout the latest README :)
Thank you! One question: I see your content of dropdown is always prerendered and persist on page. In real-world projects we have dynamically created content (like from api) or static content, but displayed with React.createPortal only if dropdown is opened (local state changed by click on trigger). It is possible to handle this case? Same question is for case when component that has ref of trigger - changed.
In prev. version of your lib i write this code
...
const [pop, setPop] = React.useState(null);
const prevTrigger = usePrevious(trigger);
const refUpdate = React.useRef(false);
useEffect(() => {
const isEqKeys = trigger?.key === prevTrigger?.key;
if (!isEqKeys) {
refUpdate.current = true;
}
if (pop && !refUpdate.current) return;
if (!refTrigger?.current || !refContent?.current) return;
if (!isOpen) return;
setPop(
new NanoPop(refTrigger.current, refContent.current, {
forceApplyOnFailure: true,
position: position || 'bottom-middle',
variantFlipOrder: {
start: 'sme',
middle: 'mse',
end: 'ems',
},
positionFlipOrder: {
top: 'tb',
right: 'rltb',
bottom: 'bt',
left: 'lrbt',
},
}),
);
refUpdate.current = false;
}, [pop, isOpen, trigger, prevTrigger, refContent?.current, refTrigger?.current]);
useEffect(() => {
// eslint-disable-next-line no-unused-expressions, @typescript-eslint/no-unused-expressions
showPortal && pop && pop.update();
}, [pop, showPortal, windowWidth, windowHeight]);
...
My current hack-workaround (with css animation + delay) https://codesandbox.io/s/distracted-haibt-15li4?file=/src/App.js Interesting your comments on this