Skip to content

Instantly share code, notes, and snippets.

@sergeydt
Last active June 8, 2022 05:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sergeydt/4b8c7bc3637098c745ccf8cad2144f25 to your computer and use it in GitHub Desktop.
Save sergeydt/4b8c7bc3637098c745ccf8cad2144f25 to your computer and use it in GitHub Desktop.
Example of React hooks for Lazy Loading modules and components (Next.js)
// Usage example:
// function MyComponent() {
// const swiperReferenceEl = useRef();
// const swiperReference = useRef();
// const swiperModule = useReached(swiperReferenceEl, async () => {
// const [ swiperJS ] = await Promise.all([import('swiper/bundle'), import('swiper/swiper-bundle.css')]);
// return swiperJS;
// })
// process.browser && useLayoutEffect(() => {
// if (!swiperModule.isInit || swiperReference.current) {
// return undefined;
// }
// swiperReference.current = new swiperModule.Swiper(swiperReferenceEl.current, {
// // ...
// });
// // ...
// }, [swiperModule]);
// }
import throttle from 'lodash/throttle'
import { useEffect, useState } from 'react';
import { isElementInViewport } from 'lib/helpers/styles';
const mockImport = () => Promise.resolve({});
export default function useReached(
ref,
moduleImport = mockImport,
delay = 7000,
) {
const [module, setModule] = useState({isInit: false});
process.browser && useEffect(() => {
if (module.isInit) {
return undefined;
}
_tryInit(false);
setTimeout(_tryInit, 1000);
const t = setInterval(() => {
_tryInit(true);
}, delay);
async function _tryInit(force = false) {
if (module.isInit || !ref?.current) {
return null;
}
if (force || isElementInViewport(ref.current)) {
clearInterval(t);
const m = await moduleImport();
setModule({ ...m, isInit: true });
}
}
const onScroll = throttle(_tryInit, 300, { leading: false });
window.addEventListener('scroll', onScroll);
return () => {
window.removeEventListener('scroll', onScroll);
}
}, [delay, module, moduleImport, ref]);
return module;
}
// Usage example:
// const LazyYoutubePlayer = withLazyReached({
// importCb: () => import('components/youtube-player'),
// });
import { useRef, forwardRef, memo } from 'react';
import dynamic from 'next/dynamic';
import useReached from 'lib/hooks/use-reached';
const mockImport = () => Promise.resolve({});
export default function withLazyReached({ importCb, delay = 7000, options = {} }) {
const Component = dynamic(importCb, options);
const fr = forwardRef((props, fRef) => {
const ref = useRef();
const reached = useReached(ref, mockImport, delay);
if (reached.isInit) {
return <Component {...props} ref={fRef} forwardedRef={fRef}/>;
} else {
return <div ref={ref}/>;
}
});
return memo(fr);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment