Skip to content

Instantly share code, notes, and snippets.

@jaredatron
Created January 25, 2023 23:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaredatron/91c9145e5e106e9a0fde3b109464559a to your computer and use it in GitHub Desktop.
Save jaredatron/91c9145e5e106e9a0fde3b109464559a to your computer and use it in GitHub Desktop.
Next.js 13 app dir like routing using React Router
import * as React from 'react'
import { createBrowserRouter, RouterProvider, Navigate, useLocation } from 'react-router-dom'
import { getLoginUrl } from '~/client/lib/urls'
import { useCurrentUser } from '~/client/hooks/auth'
import AppError from '~/client/components/AppError'
import FullPageLoading from './components/FullPageLoading'
import routeFiles from './routes/**/{Error,NotFound,Layout,Page}.js'
const routes = []
const processRoutes = (files, subRoutes, paths) => {
const fullPath = [...paths].reverse().join('/')
const { Error, NotFound, Layout, Page, ...directories } = files
let children = []
if (Page) {
let element = safeRender(Page, fullPath, 'Page.js')
if (Page.routeOptions?.topLevel){
routes.push({
path: fullPath,
element,
errorElement: Error && safeRender(Error, fullPath, 'Layout.js'),
})
}else{
children.push({ path: '', element })
}
}
for (const name in directories)
processRoutes(directories[name], children, [name, ...paths])
if (NotFound) children.push({path: '*', element: <NotFound.default />})
// TODO maybe support Layout.routeOptions.topLevel ?
subRoutes.push({
path: paths[0],
element: Layout && safeRender(Layout, fullPath, 'Layout.js'),
errorElement: Error && safeRender(Error, fullPath, 'Layout.js'),
children,
})
}
processRoutes(routeFiles, routes, [''])
export default function Router() {
const {currentUser, loading, error} = useCurrentUser()
console.log('currentUser -> ', loading ? 'LOADING' : JSON.stringify(currentUser, null, 2))
if (loading) return <FullPageLoading/>
if (error) return <AppError {...{error}}/>
const router = createBrowserRouter(routes)
global.router = router
return <RouterProvider {...{router}} />
}
function safeRender(mod, path, filename){
if (!mod.default)
return <div>Route Error: page has no default export! {path}/{filename}</div>
return mod.routeOptions?.mustBeUser
? <MustBeUser><mod.default/></MustBeUser>
: <mod.default/>
}
function MustBeUser({ children }){
const { currentUser } = useCurrentUser()
const location = useLocation()
const loginUrl = getLoginUrl(location)
return currentUser ? children : <Navigate to={loginUrl}/>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment