Skip to content

Instantly share code, notes, and snippets.

@pluma
Last active May 7, 2021 06:44
Show Gist options
  • Save pluma/4bbe8ecbd5e5cb4ab5eb73ef09e2201e to your computer and use it in GitHub Desktop.
Save pluma/4bbe8ecbd5e5cb4ab5eb73ef09e2201e to your computer and use it in GitHub Desktop.
import React from "react";
import { Link } from "react-router-dom";
import uuid from "uuid";
type Crumb = { id: string; url: string; title: string };
type Action =
| { type: "UPSERT"; payload: Crumb }
| { type: "REMOVE"; payload: { id: string } };
const breadcrumbContext = React.createContext(
undefined! as [Crumb[], (action: Action) => void]
);
export function Breadcrumb({
children,
title,
url
}: {
children: React.ReactNode;
title: string;
url: string;
}) {
const [, dispatch] = React.useContext(breadcrumbContext);
const id = React.useRef(undefined! as string);
React.useEffect(() => {
if (!id.current) id.current = uuid();
dispatch({ type: "UPSERT", payload: { id: id.current, title, url } });
}, [dispatch, url, title]);
React.useEffect(
() => () => {
dispatch({ type: "REMOVE", payload: { id: id.current } });
},
[dispatch]
);
return children as JSX.Element;
}
export function BreadcrumbsProvider({
children
}: {
children: React.ReactNode;
}) {
const [crumbs, dispatch] = React.useReducer(
(crumbs: Crumb[], action: Action): Crumb[] => {
console.log(action.type, action.payload);
switch (action.type) {
case "UPSERT":
const index = crumbs.findIndex(crumb => crumb.id === action.payload.id);
if (index === -1) return [...crumbs, action.payload];
return [...crumbs.slice(0, index), action.payload, ...crumbs.slice(index + 1)];
case "REMOVE":
return crumbs.filter(crumb => crumb.id !== action.payload.id);
}
},
[]
);
return (
<breadcrumbContext.Provider value={[crumbs, dispatch]}>
{children}
</breadcrumbContext.Provider>
);
}
export function Breadcrumbs() {
const [crumbs] = React.useContext(breadcrumbContext);
const sorted = React.useMemo(
() =>
[...crumbs].sort((a, b) => {
const a0 = a.url.split("/").length;
const b0 = b.url.split("/").length;
return a0 - b0 || a.url.length - b.url.length;
}),
[crumbs]
);
return (
<nav>
<ul>
{sorted.map(({ id, title, url }, i) => (
<li key={id}>
{i === sorted.length - 1 ? (
<strong>{title}</strong>
) : (
<Link to={{ pathname: url }}>{title}</Link>
)}
</li>
))}
</ul>
</nav>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment