Skip to content

Instantly share code, notes, and snippets.

@atomiks
Last active February 9, 2023 02:10
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save atomiks/520f4b0c7b537202a23a3059d4eec908 to your computer and use it in GitHub Desktop.
Save atomiks/520f4b0c7b537202a23a3059d4eec908 to your computer and use it in GitHub Desktop.
Lazy Tippy
// Will only render the `content` or `render` elements if the tippy is mounted to the DOM.
// Replace <Tippy /> with <LazyTippy /> component and it should work the same.
const LazyTippy = forwardRef((props, ref) => {
const [mounted, setMounted] = useState(false);
const lazyPlugin = {
fn: () => ({
onMount: () => setMounted(true),
onHidden: () => setMounted(false),
}),
};
const computedProps = {...props};
computedProps.plugins = [lazyPlugin, ...(props.plugins || [])];
if (props.render) {
computedProps.render = (...args) => (mounted ? props.render(...args) : '');
} else {
computedProps.content = mounted ? props.content : '';
}
return <Tippy {...computedProps} ref={ref} />;
});
// Partially lazy
// Will only mount all <Tippy /> content once the singleton itself is mounted.
function PartiallyLazySingleton() {
const [source, target] = useSingleton();
const [mounted, setMounted] = useState(false);
return (
<>
<Tippy
onShow={() => setMounted(true)}
onHidden={() => setMounted(false)}
singleton={source}
/>
<Tippy content={mounted ? <ExpensiveComponent /> : ''} singleton={target}>
<button>Hello</button>
</Tippy>
<Tippy content={mounted ? <ExpensiveComponent /> : ''} singleton={target}>
<button>Hello</button>
</Tippy>
</>
);
}
// Fully lazy
// Will only mount the content if that is the currently used singleton content.
function LazySingleton() {
const [source, target] = useSingleton();
const [mountedRef, setMountedRef] = useState(null);
const ref1 = useRef();
const ref2 = useRef();
return (
<>
<Tippy
onTrigger={(_, event) => setMountedRef(event.currentTarget)}
onHidden={() => setMountedRef(null)}
singleton={source}
/>
<Tippy
content={mountedRef === ref1.current ? <ExpensiveComponent /> : ''}
singleton={target}
>
<button ref={ref1}>Hello</button>
</Tippy>
<Tippy
content={mountedRef === ref2.current ? <ExpensiveComponent /> : ''}
singleton={target}
>
<button ref={ref2}>Hello</button>
</Tippy>
</>
);
}
@silouanwright
Copy link

silouanwright commented Feb 24, 2021

Using LazyTippy, I'm getting the following warning in my testing suite:

%chide() was called on a destroyed instance. This is a no-op but indicates a potential memory leak.

It won't happen running a single test, and even if I delete the one it breaks on, it'll just break on another.

Update: we unfortunately have to back out of using Tippy as our test suite doesn't allow warnings and I'm afraid we wouldn't be able to figure this out on our own.

@wahlstedt
Copy link

<LazyTippy><LazyTippy><div>Test</div></LazyTippy></LazyTippy>

^--- Nesting multiple lazy tippies gives the error "Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?"

Any ideas on how to make nesting work?

@atomiks
Copy link
Author

atomiks commented Mar 2, 2021

@danr-za
Copy link

danr-za commented Nov 7, 2021

Can some add a lazy headless singleton example?
Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment