Skip to content

Instantly share code, notes, and snippets.

@ryanflorence
Last active September 8, 2022 00:29
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save ryanflorence/02f0b85d6db989d3ede1a0007bf89133 to your computer and use it in GitHub Desktop.
Save ryanflorence/02f0b85d6db989d3ede1a0007bf89133 to your computer and use it in GitHub Desktop.
import React from 'react'
import Router from 'react-router/BrowserRouter'
import Match from 'react-router/Match'
import Link from 'react-router/Link'
import Redirect from 'react-router/Redirect'
function elementInViewport(el) {
var top = el.offsetTop
var left = el.offsetLeft
var width = el.offsetWidth
var height = el.offsetHeight
while(el.offsetParent) {
el = el.offsetParent
top += el.offsetTop
left += el.offsetLeft
}
return (
top < (window.pageYOffset + window.innerHeight) &&
left < (window.pageXOffset + window.innerWidth) &&
(top + height) > window.pageYOffset &&
(left + width) > window.pageXOffset
)
}
class InView extends React.Component {
componentDidMount() {
this.checkVisibility()
window.addEventListener('scroll', this.checkVisibility)
}
componentWillUnmount() {
window.removeEventListener('scroll', this.checkVisibility)
}
checkVisibility = () => {
if (elementInViewport(this.node)) {
this.props.onVisible()
}
}
render() {
return <div ref={n => this.node = n}>&nbsp;</div>
}
}
class Articles extends React.Component {
state = {
gists: [],
fetchedGists: []
}
componentDidMount() {
fetch('https://api.github.com/gists/public').then(res => res.json()).then(
(gists) => {
this.setState({ gists })
this.fetchGist(gists[0].id)
}
)
}
fetchGist(id) {
fetch(`https://api.github.com/gists/${id}`).then(res => res.json()).then(
(gist) => {
this.setState({
fetching: false,
fetchedGists: this.state.fetchedGists.concat([ gist ])
})
}
)
}
fetchNextGist = () => {
this.setState({ fetching: true })
const { fetchedGists, gists } = this.state
const lastFetched = fetchedGists[fetchedGists.length - 1]
const index = gists.indexOf(gists.find(gist => gist.id === lastFetched.id))
this.fetchGist(gists[index+1].id)
}
render() {
const { gists, fetchedGists, fetching } = this.state
return (
<Router>
<div>
{fetchedGists.map((gist, index) => (
<article style={{ margin: '20px', border: '1px solid', padding: '20px' }}>
<h1>{gist.description || 'No Description'}</h1>
{Object.keys(gist.files).map(fileName => (
<div>
<h2>{fileName}</h2>
<pre>{gist.files[fileName].content}</pre>
</div>
))}
{index === fetchedGists.length - 1 && (
<Redirect to={`/gists/${gist.id}`} replace={true}/>
)}
</article>
))}
{!fetching && fetchedGists.length && (gists.length > fetchedGists.length) && (
<InView onVisible={this.fetchNextGist}/>
)}
</div>
</Router>
)
}
}
export default Articles
@ryanflorence
Copy link
Author

I hope <Redirect/> is starting to make sense to people! I'M IN LOOOOOOOOOOOVE

@tylermcginnis
Copy link

Amazing.

@MrOrz
Copy link

MrOrz commented Nov 22, 2016

Awesome.

Replacing this part:

  var top = el.offsetTop
  var left = el.offsetLeft
  var width = el.offsetWidth
  var height = el.offsetHeight

with el.getBoundingClientRect() can be even better, since the latter reduces browser reflow to only 1 time, and it returns a rect that is relative to the viewport, making the in-view checking logic simpler.

@suhaotian
Copy link

Great!!

@pospospos2007
Copy link

sad,i can't figure out what does it mean. It looks like awesome:)

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