我尝试实现自定义 useBreadcrumbs 钩子,它应该在路由器组件挂载时添加面包屑条目,并在卸载时删除该条目。 我尝试使用上下文提供程序和一些方法来做到这一点:
const BreadcrumbsProvider: FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [breadcrumbConfiguration, setBreadcrumbConfiguration] = useState<
BreadcrumbOptions
>({} as BreadcrumbOptions);
// adds 1 entry to the entries array end
const updateBreadcrumbs = useCallback((path: string, name: string) => {
setBreadcrumbConfiguration((prev) => {
const entries = prev?.entries || [];
if (
!entries.some((entry) => entry.path === path && entry.name === name)
) {
return {
...prev,
entries: [...entries, { path, name }],
};
}
return { ...prev };
});
}, []);
//removes entry
const revertOnUnmount = useCallback((path: string, name: string) => {
setBreadcrumbConfiguration((prev) => {
const entries = prev?.entries || [];
if (entries.some((entry) => entry.path === path && entry.name === name)) {
return {
...prev,
entries: [...entries.slice(0, entries.length - 1)],
};
}
return { ...prev };
});
}, []);
const val = useMemo(() => {
return { breadcrumbConfiguration, revertOnUnmount, updateBreadcrumbs };
}, [breadcrumbConfiguration]);
return (
<BreadcrumbContext.Provider value={val}>
{children}
</BreadcrumbContext.Provider>
);
};
export default BreadcrumbsProvider;
和我的 useBreadcrumbs 钩子:
export const useBreadcrumbs = (path: string, name: string) => {
const {
breadcrumbConfiguration,
updateBreadcrumbs,
revertOnUnmount,
} = useContext(BreadcrumbContext);
useEffect(() => {
updateBreadcrumbs(path, name);
return () => revertOnUnmount(path, name);
}, []);
};
所以在我的组件中,我是这样使用的:
const NestingExample = memo(() => {
console.log('render app');
return (
<Router>
<BreadcrumbsProvider>
<div>
<Breadcrumbs />
<ul>
<li>
<Link to="/topics">Topics</Link>
</li>
</ul>
<Switch>
<Route path="/topics">
<Topics />
</Route>
</Switch>
</div>
</BreadcrumbsProvider>
</Router>
);
});
const Home = memo(() => {
console.log('render home page');
useBreadcrumbs('/path-to-home', 'home page'); // <--
return (
<div>
<h2>Home</h2>
</div>
);
});
const Topics = memo(() => {
let { path, url } = useRouteMatch();
useBreadcrumbs('/path-to-topics', 'topics page'); // <--
return (
<div>
<h2>Topics</h2>
<ul>
<li>
<Link to={`${url}/rendering`}>Rendering with React</Link>
</li>
<li>
<Link to={`${url}/components`}>Components</Link>
</li>
</ul>
<Switch>
<Route exact path={path}>
<h3>Please select a topic.</h3>
</Route>
<Route path={`${path}/:topicId`} component={() => <Topic />} />
</Switch>
</div>
);
});
const Topic = memo(() => {
let { topicId } = useParams();
useBreadcrumbs('/inner-topic', `inner topic ${topicId}`); // <--
return (
<div>
<h3>{topicId}</h3>
</div>
);
});
const Template: Story<BreadcrumbsProps> = () => {
return <NestingExample />;
};
export const Test: Story<BreadcrumbsProps> = Template.bind({});
所以,它适用于第一级路线,但是当我点击带有确切主题的内部路线时 - 它进入无限循环
附注。由于某些情况,我在故事书中这样做。 PSS。 sry for textwall