import * as React from 'react' ; import { Switch , Route , withRouter , Redirect , match as Match , RouteComponentProps , } from 'react-router-dom' ; import { loadInitialProps } from './loadInitialProps' ; import { History , Location } from 'history' ; import { AsyncRouteProps , ServerAppState , InitialData , TransitionBehavior , CtxBase , } from './types' ; import { get404Component , getAllRoutes , isInstantTransition } from './utils' ; import { loadStaticProps } from './loadStaticProps' ; export interface AfterpartyProps extends RouteComponentProps < any > { history : History ; location : Location ; data : ServerAppState ; routes : AsyncRouteProps [ ] ; match : Match < any > ; transitionBehavior : TransitionBehavior ; } export interface AfterpartyState { data ? : InitialData ; previousLocation : Location | null ; currentLocation : Location | null ; isLoading : boolean ; } class Afterparty extends React . Component < AfterpartyProps , AfterpartyState > { state = { data : this . props . data . initialData , previousLocation : null , currentLocation : this . props . location , isLoading : false } ; prefetcherCache : object = { } ; NotfoundComponent : React . ComponentType < RouteComponentProps < any > > | React . ComponentType < any > = get404Component ( this . props . routes ) ; static defaultProps = { transitionBehavior : 'blocking' as TransitionBehavior } ; static getDerivedStateFromProps ( props , state ) { const currentLocation = props . location ; const previousLocation = state . currentLocation ; const navigated = currentLocation !== previousLocation ; if ( navigated ) { return { previousLocation : state . previousLocation || previousLocation , currentLocation , isLoading : true } ; } return null ; } componentDidUpdate ( _prevProps , prevState ) { const navigated = prevState . currentLocation !== this . state . currentLocation ; if ( navigated ) { const { location , history , routes , data , transitionBehavior , match , staticContext , children , ... rest } = this . props ; const { scrollToTop , ssg } = data . afterData ; const isInstantMode = isInstantTransition ( transitionBehavior ) ; const isBloackedMode = ! isInstantMode ; const ctx = { location , history , scrollToTop , ... rest } ; const isPageChanged = ! ! prevState . currentLocation && prevState . currentLocation . pathname !== location . pathname ; const isAllowedToScroll = isPageChanged && scrollToTop . current === true ; if ( isInstantMode && isAllowedToScroll ) { window . scrollTo ( 0 , 0 ) ; } const loadData = ssg ? loadStaticProps : loadInitialProps ; loadData ( location . pathname , routes , ctx ) . then ( res => res . data ) . then ( ( data ) => { if ( this . state . currentLocation !== location ) return ; if ( isBloackedMode && isAllowedToScroll ) { window . scrollTo ( 0 , 0 ) ; } this . setState ( { previousLocation : null , data , isLoading : false } ) ; } ) . catch ( ( e ) => { console . log ( e ) ; } ) ; } } prefetch = ( pathname ) => { const { ssg } = this . props . data . afterData ; const loadData = ssg ? loadStaticProps : loadInitialProps ; loadData ( pathname , this . props . routes , { history : this . props . history } ) . then ( ( { data } ) => { this . prefetcherCache = { ... this . prefetcherCache , [ pathname ] : data } ; } ) . catch ( e => console . log ( e ) ) ; } ; render ( ) { const { previousLocation , data , isLoading } = this . state ; const { location : currentLocation , transitionBehavior } = this . props ; const initialData = this . prefetcherCache [ currentLocation . pathname ] || data ; const instantMode = isInstantTransition ( transitionBehavior ) ; const location = instantMode ? currentLocation : previousLocation || currentLocation ; return ( < Switch location = { location } > { initialData ?. statusCode === 404 && ( < Route component = { this . NotfoundComponent } path = { location . pathname } / > ) } { initialData ?. redirectTo && < Redirect to = { initialData . redirectTo } />} { getAllRoutes ( this . props . routes ) . map ( ( r , i ) => ( < Route key = { `route--${ i }` } path = { r . path } exact = { r . exact } render = { props => React . createElement ( r . component , { ... initialData , history : props . history , match : props . match , prefetch : this . prefetch , location , isLoading } ) } / > ) ) } < /Switch>); } } export const After = withRouter ( Afterparty ) ;