Skip to content

Instantly share code, notes, and snippets.

@eiriklv
Created April 15, 2023 00:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eiriklv/1e6672c7987a6db47bf37bc604360bbc to your computer and use it in GitHub Desktop.
Save eiriklv/1e6672c7987a6db47bf37bc604360bbc to your computer and use it in GitHub Desktop.
React router
import { History, Location } from "history";
import { MatchResult } from "path-to-regexp";
import { ReactElement, useContext, useEffect, useState } from "react";
import React from "react";
import { getPathMatch } from "./utils/routing";
export interface RouterContextInterface {
history?: History;
location?: Location;
basePath: string;
}
export const routerContextDefaultValue: RouterContextInterface = {
basePath: ''
};
export const RouterContext = React.createContext<RouterContextInterface>(
routerContextDefaultValue
);
export type RouterProps = {
history: History;
children: ReactElement;
basePath?: string;
};
export function Router({ history, children, basePath = '' }: RouterProps) {
const [location, setCurrentLocation] = useState<Location>(history.location);
useEffect(() => {
return history.listen(() => {
setCurrentLocation(history.location);
});
});
return (
<RouterContext.Provider value={{ history, location, basePath }}>
{children}
</RouterContext.Provider>
);
}
export interface RouteContextInterface {
match?: MatchResult;
}
export const routeContextDefaultValue: RouteContextInterface = {};
export const RouteContext = React.createContext<RouteContextInterface>(
routeContextDefaultValue
);
export type RouteProps = {
path: string;
exact?: boolean;
children: ReactElement;
};
export function Route({ path, exact, children }: RouteProps) {
const { location, basePath } = useContext(RouterContext);
const pathname = location?.pathname || "";
const pathToMatch = basePath + path;
console.log({ pathname, pathToMatch, path })
const pathMatch = getPathMatch(pathToMatch, pathname);
const isMatch = exact ? pathToMatch === pathname : !!pathMatch;
if (isMatch && pathMatch) {
console.log("component route match", pathMatch);
return (
<RouteContext.Provider value={{ match: { ...pathMatch, path: pathMatch.path.split(basePath)[1] } }}>
{children}
</RouteContext.Provider>
);
}
return null;
}
export type LinkProps = {
to: string;
children: ReactElement
};
export function Link({ to, children }: LinkProps) {
const history = useHistory();
const basePath = useBasePath();
const fullPath = basePath + to;
const handleClick = (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
event.preventDefault();
history.push(fullPath);
}
return (
<a href={fullPath} onClick={handleClick}>
{children}
</a>
)
}
export const useMatch = () => {
const { match } = useContext(RouteContext);
return match as MatchResult;
};
export const useLocation = () => {
const { location } = useContext(RouterContext);
return location as Location;
};
export const useHistory = () => {
const { history } = useContext(RouterContext);
return history as History;
};
export const useBasePath = () => {
const { basePath } = useContext(RouterContext);
return basePath;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment