Last active
November 25, 2020 11:03
-
-
Save danielknell/45d9a8dd491f5988fec038eb11ebc00d to your computer and use it in GitHub Desktop.
Config based routing in react.
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 { BrowserRouter as Router } from "react-router-dom"; | |
import { Routes, Link, RouteDefinition } from "./router"; | |
const Home: React.FC<{}> = () => { | |
return ( | |
<> | |
<h1>Home</h1> | |
<p><Link name="hello" params={{name: "World"}}>Hello World</Link></p> | |
<p><Link name="hello" params={{name: "Bob"}}>Hello Bob</Link></p> | |
</> | |
); | |
}; | |
const Hello: React.FC<{ name: string }> = ({ name }) => { | |
return ( | |
<> | |
<h1>Hello { name }!</h1> | |
<p><Link name="home">Go Home</Link></p> | |
</> | |
); | |
}; | |
const routes: RouteDefinition[] = [ | |
{ | |
name: "home", | |
path: "/", | |
render: () => <HomeScene />, | |
}, | |
{ | |
name: "hello", | |
path: "/hello/:name", | |
render: ({ match }) => <Hello name={match.params.name} />, | |
}, | |
{ | |
name: "error.404", | |
render: () => <h1>Not Found</h1>, | |
}, | |
]; | |
const Application = () => { | |
return ( | |
<Router> | |
<Routes routes={routes} /> | |
</Router> | |
); | |
}; |
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 { | |
Link as BaseLink, | |
NavLink as BaseNavLink, | |
Switch, | |
Route, | |
generatePath, | |
RouteComponentProps, | |
RouteChildrenProps, | |
match, | |
} from "react-router-dom"; | |
import { LocationState, Location } from "history"; | |
interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> { | |
component?: React.ComponentType<any>; | |
name: string; | |
params?: { [paramName: string]: string | number | boolean | undefined }; | |
replace?: boolean; | |
innerRef?: React.Ref<HTMLAnchorElement>; | |
} | |
interface NavLinkProps<S = LocationState> extends LinkProps { | |
activeClassName?: string; | |
activeStyle?: React.CSSProperties; | |
exact?: boolean; | |
strict?: boolean; | |
isActive?<Params extends { [K in keyof Params]?: string }>( | |
match: match<Params> | null, | |
location: Location<S> | |
): boolean; | |
location?: Location<S>; | |
} | |
export interface RouteDefinition { | |
name: string; | |
component?: | |
| React.ComponentType<RouteComponentProps<any>> | |
| React.ComponentType<any>; | |
render?: (props: RouteComponentProps<any>) => React.ReactNode; | |
children?: | |
| ((props: RouteChildrenProps<any>) => React.ReactNode) | |
| React.ReactNode; | |
path?: string; | |
exact?: boolean; | |
sensitive?: boolean; | |
strict?: boolean; | |
} | |
interface RouterProps { | |
routes: RouteDefinition[]; | |
} | |
const RouteContext = React.createContext<RouteDefinition[]>([]); | |
export const Routes: React.FC<RouterProps> = ({ routes }) => { | |
return ( | |
<RouteContext.Provider value={routes}> | |
<Switch> | |
{routes.map(({ name, exact, ...props }) => ( | |
<Route | |
key={name} | |
exact={exact === undefined ? true : exact} | |
{...props} | |
/> | |
))} | |
</Switch> | |
</RouteContext.Provider> | |
); | |
}; | |
const useRoute = ( | |
name: string, | |
params?: { [paramName: string]: string | number | boolean | undefined } | |
): string => { | |
const routes = React.useContext(RouteContext); | |
const mapping = React.useMemo(() => { | |
return Object.fromEntries(routes.map((v) => [v.name, v])); | |
}, [routes]); | |
const route = mapping[name]; | |
if (route === undefined) { | |
throw new Error(`Unknown route: ${name}`); | |
} | |
if (route.path === undefined) { | |
throw new Error(`No path defined for route: ${name}`); | |
} | |
return generatePath(route.path, params); | |
}; | |
export const Link: React.FC<LinkProps> = ({ name, params = {}, ...props }) => { | |
const path = useRoute(name, params); | |
return <BaseLink to={path} {...props} />; | |
}; | |
export const NavLink: React.FC<NavLinkProps> = ({ | |
name, | |
params = {}, | |
...props | |
}) => { | |
const path = useRoute(name, params); | |
return <BaseNavLink to={path} {...props} />; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment