Skip to content

Instantly share code, notes, and snippets.

@ryanflorence
Created April 13, 2016 15:19
Show Gist options
  • Select an option

  • Save ryanflorence/eac32536cddcb0c13a10617285c14998 to your computer and use it in GitHub Desktop.

Select an option

Save ryanflorence/eac32536cddcb0c13a10617285c14998 to your computer and use it in GitHub Desktop.
import React from 'react'
import { findDOMNode } from 'react-dom'
if (typeof window !== 'undefined' && 'scrollRestoration' in window.history) {
history.scrollRestoration = 'manual'
}
export const useRestoreScroll = () => ({
renderRootContainer: (props) => (
<RestoreScrollContainer {...props}/>
)
})
const { shape, func, string } = React.PropTypes
const restoreScrollContextType = {
restoreScroll: shape({
registerScroller: func,
getPosition: func
}).isRequired
}
const RestoreScrollContainer = React.createClass({
childContextTypes: restoreScrollContextType,
getChildContext() {
return { restoreScroll: {
registerScroller: (scrollKey, component) => {
this.scrollers[scrollKey] = component
},
unregisterScroller: (scrollKey) => {
delete this.scrollers[scrollKey]
},
getPosition: this.getPosition
} }
},
componentWillMount() {
// {
// [location.key]: {
// window: { scrollX, scrollY },
// [scrollKey]: { scrollTop, scrollLeft }
// },
// [location.key]: etc...
// }
this.positionsByLocation = {}
this.scrollers = {}
this.updateScrollOnUpdate = false
},
componentWillReceiveProps(nextProps) {
if (nextProps.location !== this.props.location) {
this.saveScrollerPositions()
}
},
getPosition(scrollKey) {
const { positionsByLocation } = this
const { key } = this.props.location
const locationPositions = positionsByLocation[key]
return locationPositions ? locationPositions[scrollKey] || null : null
},
savePosition(scrollKey, position) {
this.positionsByLocation[this.props.location.key][scrollKey] = position
},
saveScrollerPositions() {
const { positionsByLocation, scrollers } = this
const { key } = this.props.location
if (!positionsByLocation[key])
positionsByLocation[key] = {}
const { scrollY, scrollX } = window
this.savePosition('window', { scrollX, scrollY })
for (const scrollKey in scrollers) {
const scrollerNode = scrollers[scrollKey]
const { scrollTop, scrollLeft } = findDOMNode(scrollerNode)
this.savePosition(scrollKey, { scrollTop, scrollLeft })
}
},
componentDidUpdate(prevProps) {
if (prevProps.location !== this.props.location) {
const position = this.getPosition('window')
if (position) {
const { scrollX, scrollY } = position
window.scrollTo(scrollX, scrollY)
}
}
},
render() {
const { render, ...props } = this.props
return render(props)
}
})
export const RestoreScroll = React.createClass({
contextTypes: restoreScrollContextType,
propTypes: {
scrollKey: string.isRequired
},
componentDidMount() {
const { registerScroller } = this.context.restoreScroll
const { scrollKey } = this.props
registerScroller(scrollKey, this)
this.restoreScrollPosition()
},
componentWillUnmount() {
const { unregisterScroller } = this.context.restoreScroll
const { scrollKey } = this.props
unregisterScroller(scrollKey)
},
restoreScrollPosition() {
const { scrollKey } = this.props
const { getPosition } = this.context.restoreScroll
const position = getPosition(scrollKey)
if (position) {
const node = findDOMNode(this)
const { scrollTop, scrollLeft } = position
node.scrollTop = scrollTop
node.scrollLeft = scrollLeft
}
},
render() {
return React.Children.only(this.props.children)
}
})
import React from 'react'
import { render, findDOMNode } from 'react-dom'
import { Router, Route, IndexRoute, browserHistory, Link } from 'react-router'
import applyMiddleware from 'react-router-apply-middleware'
import { useRestoreScroll, RestoreScroll } from 'react-router-restore-scroll'
const App = React.createClass({
render() {
return (
<div style={{ fontFamily: 'sans-serif', fontWeight: '200' }}>
<h1>Restore Scroll!</h1>
<ul>
<li><Link to="/">Index</Link></li>
<li><Link to="/page/1">Page 1</Link></li>
<li><Link to="/page/2">Page 2</Link></li>
</ul>
{this.props.children}
</div>
)
}
})
const Index = React.createClass({
render() {
return (
<div>
<h2>Index</h2>
<RestoreScroll scrollKey="one">
<div style={{ height: '200px', overflow: 'auto', border: '1px solid' }}>
<div style={{ height: '100px', background: 'hsl(0, 50%, 90%)' }}>scroll me</div>
<div style={{ height: '100px', background: 'hsl(100, 50%, 90%)' }}>two</div>
<div style={{ height: '100px', background: 'hsl(200, 50%, 90%)' }}>three</div>
</div>
</RestoreScroll>
</div>
)
}
})
const Page = React.createClass({
render() {
const { page } = this.props.params
return (
<div>
<h2>Chapter {page}</h2>
<div style={{ height: '50vh', background: 'hsl(0, 50%, 90%)' }}>scroll down</div>
<div style={{ height: '50vh', background: 'hsl(100, 50%, 90%)' }}>one</div>
<div style={{ height: '50vh', background: 'hsl(200, 50%, 90%)' }}>two</div>
<div style={{ height: '50vh', background: 'hsl(300, 50%, 90%)' }}>click the back button</div>
</div>
)
}
})
const routes = (
<Route path="/" component={App}>
<IndexRoute component={Index}/>
<Route path="page/:page" component={Page}/>
</Route>
)
render(
<Router
render={applyMiddleware(useRestoreScroll())}
history={browserHistory}
routes={routes}
/>,
document.getElementById('app')
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment