Last active
June 3, 2021 08:11
-
-
Save Icohen007/bcffa02dc48bda1cbf26d2e04e2c1f68 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useRef, useEffect, useState } from 'react'; | |
function debounce(func, wait) { | |
let timeout; | |
return function fn(...args) { | |
const context = this; | |
clearTimeout(timeout); | |
timeout = setTimeout(() => func.apply(context, args), wait); | |
}; | |
} | |
const useIntersection = (scrollRef) => { | |
const [event, setEvent] = useState(null); | |
const scrollObserver = (node) => { | |
new IntersectionObserver((entries) => { | |
entries.forEach((entry) => { | |
if (entry.intersectionRatio > 0) { | |
setEvent(entry); | |
} | |
}); | |
}).observe(node); | |
}; | |
useEffect(() => { | |
if (scrollRef.current) { | |
scrollObserver(scrollRef.current); | |
} | |
}, [scrollRef]); | |
return event; | |
}; | |
const usePullToRefresh = (id, threshold = 100) => { | |
const [pullFromBottom, setPullFromBottom] = useState(false); | |
const [momentumY, setMomentumY] = useState(0); | |
useEffect(() => { | |
// Disable the event when the wheel momentum slows down | |
if (pullFromBottom && momentumY < 10) { | |
setPullFromBottom(false); | |
} | |
}, [pullFromBottom, momentumY]); | |
const triggerEvent = debounce(() => { | |
// Wait for the deceleration of the wheel momentum to enhance the scrolling UX. | |
// The setTimeout can be removed without having too much impact. | |
setTimeout(() => { | |
setPullFromBottom(true); | |
}, 100); | |
}, 500); | |
const onWheel = (event) => { | |
const appHeight = document.getElementById(id).offsetHeight; | |
const winHeight = window.innerHeight; | |
const maxScrollY = appHeight - winHeight; | |
const scrollY = window.pageYOffset; | |
const atBottom = scrollY >= maxScrollY; | |
setMomentumY(event.deltaY); | |
// Pull harder to trigger the event. | |
if (atBottom && event.deltaY > threshold) { | |
triggerEvent(); | |
} | |
}; | |
useEffect(() => { | |
window.addEventListener('wheel', onWheel); | |
return () => { | |
window.removeEventListener('wheel', onWheel); | |
}; | |
}, []); | |
return pullFromBottom; | |
}; | |
const initialElements = ['Hey', 'You', 'Whats', 'Up']; | |
const InfiniteScroll = ({ | |
children, className, onTop, onBottom, appId = '__next', | |
}) => { | |
const scrollTopRef = useRef(null); | |
const scrollBottomRef = useRef(null); | |
const reachTop = useIntersection(scrollTopRef); | |
const reachBottom = useIntersection(scrollBottomRef); | |
const pullFromBottom = usePullToRefresh(appId, 100); | |
useEffect(() => { | |
if (!onTop) { | |
return; | |
} | |
if (reachTop) { | |
onTop(reachTop); | |
} | |
}, [reachTop]); | |
useEffect(() => { | |
if (!onBottom) { | |
return; | |
} | |
if (reachBottom || pullFromBottom) { | |
onBottom({ reachBottom, pullFromBottom }); | |
} | |
}, [reachBottom, pullFromBottom]); | |
return ( | |
<div className={className}> | |
<div ref={scrollTopRef} className="checkpoint top" /> | |
{children} | |
<div ref={scrollBottomRef} className="checkpoint bottom" /> | |
</div> | |
); | |
}; | |
export const Example = () => { | |
const [elements, setElements] = useState([0, ...initialElements]); | |
const [count, setCount] = useState(1); | |
const handleBottom = ({ pullFromBottom }) => { | |
if (pullFromBottom) { | |
setElements((prevElements) => [ | |
...prevElements, | |
count, | |
...initialElements, | |
]); | |
setCount((prevCount) => prevCount + 1); | |
} | |
}; | |
return ( | |
<div className="App"> | |
<h1>Infinity Scroll Example</h1> | |
<h2>Start Scroll to see some magic happen!</h2> | |
<InfiniteScroll onBottom={handleBottom}> | |
<p> | |
Hey Hey Hey Hey Hey Hey Hey Hey Hey Hey Hey Hey Hey Hey Hey Hey Hey | |
Hey Hey Hey Hey Hey | |
{' '} | |
</p> | |
{elements.map((elem, i) => ( | |
<p key={i}>{elem}</p> | |
))} | |
</InfiniteScroll> | |
</div> | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment