Repository URL to install this package:
|
Version:
1.3.2 ▾
|
/**
* @todo console.group these
*
* @throws LogicException
*
* 1. renders routes
* @example /myaccount
* - this calls
* 2. renders subroutes
* @example /myaccount/orders
* a. checks if the route params HAS subroutes
* b. checks the route.component.getComponentForRoute
* c. else, route.component.getComponentForSubRoute
*
*
* @module Router
*
* @variation minimal can be
* 1. create a flat array
* 2. sort by length (longest, shortest)
*
* @variation
* 1. use context
* 2. if context.subroutes
* - render the first to match
* - then it would keep traversing
*
*/
import React from 'react'
import { OneRouterContainer, oneRouter } from '@skava/router'
import { isObj, EMPTY_OBJ } from 'exotic'
import {
array,
object,
string,
number,
any,
} from '@skava/modules/___dist/view-container/types'
import { Switch, Route, Redirect, withRouter } from 'react-router-dom'
import {
renderRoutes,
matchRoutes,
} from '@skava/router/dist/react-router-config'
import { toComponentName } from '@skava/modules/___dist/identifier'
import { Fallback } from 'views/pages/Fallback'
import routeList from 'routes'
import { setup } from 'src/bootstrapper/setup/observableRouting'
import ErrorBoundary from '../ErrorBoundary'
// @@demo
// wasn't happening...
setup()
/**
* used to DI the current route
*/
const scoped = {
params: undefined,
// doubly linked list would be great for page animations here
prevRoute: EMPTY_OBJ,
prevView: undefined,
prevLocation: undefined,
// nextView: undefined,
}
function fromPropsToPrevLocation(props) {
// props.location.key
const url = props.match ? props.match.url : ''
const params = props.match ? JSON.stringify(props.match.params) : ''
const hashed =
url +
props.location.pathname +
props.location.hash +
props.location.search +
params
if (typeof window === 'object') {
return hashed + window.location.href
} else {
return hashed
}
}
/**
* @todo really need to optimize `entries`
* @todo @fixme this is temporary
* @description change 1router .entries
* to use the scoped params for this route
* (.matched is a getter)
*/
function tapOneRouter() {
// only tap 1x
if (oneRouter.entries.isTapped) {
return
}
// oneRouter.notify = action
function entriesTapped() {
// console.log('[routing] calling tapped entries')
// @note - we could use [v] before overriding
// but we can use `this.toObj` which is why that fn was method that way
// (protected name, entries being aliased)
// const entries = oneRouter.entries
const obj = oneRouter.toObj()
// update when scoped params updates
Object.assign(obj, scoped.params)
return obj
}
// make sure we only tap once
entriesTapped.isTapped = true
// set on the oneRouter
oneRouter.entries = entriesTapped
}
// init
tapOneRouter()
function debugRouting(props) {
console.log('[ROUTING] - mounting OneRouterContainer')
// console.log(oneRouter.entries())
// console.log('ROUTING_PROPS_FROM_ROUTER')
// console.log(props)
}
// component
// @NOTE !!! USING @withRouter HERE PUTS A ROUTE INSIDE THE ROUTE!!!!!!
class OneRouterContainerTemporary extends React.PureComponent {
static contextTypes = {
router: object,
}
componentWillMount() {
const params = this.props.match.params
scoped.params = params
debugRouting(this.props)
}
componentDidMount() {
// only doing this on the browser
oneRouter.router = this.context.router || this.props.router
}
render() {
return ''
}
}
/**
* @description decorate a route, DI the params out into 1router
*/
// @todo @split
// eslint-disable-next-line max-statements
function toRouteItem(route) {
const { component, path, exact } = route
const Component = component
// named route - this wraps the route
// @note this is basically @withRouter & setting oneRouter props from the props
// @todo @lint @split @@demo
// eslint-disable-next-line
function decoratedRouteComponent(props) {
// !!! probably most of this is not needed since we are not deduping
// ^ however, we can keep as comment for route transitions
// ^ thanks to removing @withRouter
// always update 1router
scoped.oneRouterView = (
<OneRouterContainerTemporary {...props} decoratedRouteItem={route} />
)
const prevLocation = fromPropsToPrevLocation(props)
// I see many duplicate route updates, no good - use previously rendered
if (
route.path === scoped.prevRoute.path &&
scoped.prevLocation === prevLocation
) {
// todo
console.log('[ROUTING] deduped route ', route)
return scoped.prevView
}
// .render
console.log('[ROUTING] rendering route: ', route)
oneRouter.observable.urlList.push(prevLocation)
oneRouter.observable.url = prevLocation
scoped.prevLocation = prevLocation
scoped.prevRoute = route
scoped.prevView = (
<React.Fragment key={path}>
{scoped.oneRouterView}
<Component {...props} />
</React.Fragment>
)
return scoped.prevView
}
// if (process.env.DEBUG_REACT_DISPLAY_NAME) {
const componentName = toComponentName(Component)
const wrapName = `decoratedRouteComponent`
if (componentName.includes(wrapName)) {
console.warn('[ROUTING] component duplicated in mapping - fix: ', wrapName)
return route
}
const displayName = `${wrapName}(${componentName})`
decoratedRouteComponent.displayName = displayName
route.component = decoratedRouteComponent
return route
}
/**
* @description take all routes, add 1router extractor for DI
*/
function toRoutesWithOneRouterUpdater(routes) {
return routes.map(toRouteItem)
}
/**
* @description OneRouterRouter
*/
class Routing extends React.PureComponent {
static propTypes = {
routes: array.isRequired,
location: object.isRequired,
}
static defaultProps = {
routes: routeList,
location: EMPTY_OBJ,
}
constructor(props, ...args) {
super(props, ...args)
// no need to make it update, only needs to happen 1x
this.routesWithOneRouterUpdater = toRoutesWithOneRouterUpdater(
this.props.routes
)
}
componentDidMount() {
if (typeof window === 'object') {
window.scrollTo({
top: 0,
behavior: 'smooth',
})
}
}
render() {
const { routesWithOneRouterUpdater } = this
return (
<ErrorBoundary>{renderRoutes(routesWithOneRouterUpdater)}</ErrorBoundary>
)
}
}
export { Routing }
export default Routing