React Progress Bars
13th Sep - 2019Progress bars make a blog look cooler. More importantly, it lets the reader know that they’re almost finished with the article, which if good, if you’re overstimulated.
They’re easy to add, I’ve added one to lumen, the template that this blog is based on.
First, add the code to make it work. You’re probably looking for this -
class Page extends React.Component {
constructor(props) {
super(props)
this.state = {progress: '0%'}
this.setScroll = this.setScroll.bind(this)
}
componentDidMount() {
this.setScroll()
window.addEventListener('scroll', this.setScroll)
}
componentWillUnmount() {
window.removeEventListener('scroll', this.setScroll)
}
setScroll() {
const winScroll = document.body.scrollTop || document.documentElement.scrollTop
const height = document.documentElement.scrollHeight - document.documentElement.clientHeight
const scrolled = (winScroll / height) * 100
const scrollPerc = `${scrolled}%`
this.setState({ scrollPerc })
}
}
Here, we set an initial state - scrolled 0%. We also add an event listener to update the state on scroll. The magic happens within setScroll()
— we calculate the scroll position and the height of the window. Height it calculated as scrollHeight - clientHeight
so that the bar starts at 0, and when we hit the bottom of the page, the bar hits 100%.
Then, we sprinkle in a bit of JSX -
<div className="post-progress" style={{width: this.state.scrollPerc}}
And a bit of styling to boot -
.post-progress {
position: fixed;
height: 3px;
top: 0;
left: 0;
background: $color-primary;
}
By updating state on each scroll, the component re-renders and div.post-progress
gets wider (or thinner) based on the scroll, looking like a scrollbar.
The code examples are used ad verbatim in this blog - give them a go!
Update (20-Sep-2019): Migrated to Hooks
Since migrating the site to hooks, The code for this is significantly cleaner.
The component is done with a functional component, with a custom useScroll
hook. It looks like this:
function useScroll() {
const [scrollPerc, setScrollPerc] = useState(getScrollPerc())
const setScroll = () => setScrollPerc(getScrollPerc())
useEffect(() => {
window.addEventListener('scroll', setScroll)
return () => window.removeEventListener('scroll', setScroll)
})
return scrollPerc
}
The original getScrollPerc()
from before is used. It’s used by setScroll
, a higher-order function. This means that an event listener can be added and removed.
useEffect
is used to set an event listener that updates the state and by returning window.removeEventListener(......)
, the component can cleanup on unmount.