import { useState , useEffect , createElement , useRef , useCallback , Fragment } from 'rax' ; import useCreation from './useCreation' ; import RouteMatcher from './RouteMatcher' ; import { IRoute } from '../type' ; type UpdaterType = ( error : any , component ? : JSX . Element ) => void ; export default function KeepAliveRouter ( { history , routes } ) { const currentPathName = history . location . pathname ; const keepAliveRoutesRef = useRef ( [ ] ) ; const appendKeepAliveRoute = useCallback ( ( route ) => { const newKeepAliveRoutes = [ ... keepAliveRoutesRef . current , route , ] ; setKeepAliveRoutes ( newKeepAliveRoutes ) ; keepAliveRoutesRef . current = newKeepAliveRoutes ; } , [ ] ) ; const initialRoutes = useCreation ( ( ) => { const matcher = new RouteMatcher ( routes ) ; const route = matcher . match ( currentPathName ) ; if ( ! route ) { return [ ] ; } if ( isPromise ( route ) ) { loadAsyncComponent ( ( ) => route . component , ( err , component ) => { if ( err ) { throw err ; } appendKeepAliveRoute ( { ... route , component , } ) ; } ) ; return [ ] ; } return [ route ] ; } ) ; const [ keepAliveRoutes , setKeepAliveRoutes ] = useState ( initialRoutes ) ; const [ , setForceUpdate ] = useState ( 0 ) ; useEffect ( ( ) => { const matcher = new RouteMatcher ( routes ) ; const unlisten = history . listen ( ( location ) => { const { pathname } = location ; const targetRoute = matcher . match ( pathname ) ; if ( targetRoute && targetRoute . keepAlive ) { if ( keepAliveRoutesRef . current . find ( ( { path } ) => path === pathname ) ) { setForceUpdate ( Math . random ( ) ) ; } else if ( isPromise ( targetRoute ) ) { loadAsyncComponent ( ( ) => targetRoute . component , ( err , component ) => { if ( err ) { throw err ; } appendKeepAliveRoute ( { ... targetRoute , component , } ) ; } ) ; } else { appendKeepAliveRoute ( targetRoute ) ; } } else { setForceUpdate ( Math . random ( ) ) ; } } ) ; return unlisten ; } , [ ] ) ; return ( < Fragment > { keepAliveRoutes . map ( ( { path , component } ) => < div key = { path } style = { { display : path === currentPathName ? 'unset' : 'none' } } > { component } < /div>)} < /Fragment>); } function isPromise ( route ) { return route . component instanceof Promise ; } function loadAsyncComponent ( dynamicImport , updater ) { dynamicImport ( ) . then ( ( component ) => { updater ( null , component ) ; } ) . catch ( ( err ) => { updater ( err ) ; } ) ; }