Created
April 13, 2016 15:19
-
-
Save ryanflorence/eac32536cddcb0c13a10617285c14998 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 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) | |
| } | |
| }) | |
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 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