我编写了一个面包屑组件(RouteBreadcrumbs),它具有以下特性/行为:
['Settings', 'System settings', 'Display'] )。该组件按照放置在数组中的顺序显示这些标签。/system-settings/display,则组件不会呈现。最终,组件呈现时会像这样(我还没有实现每个面包屑之间的箭头):
Settings > System settings > Display在这种情况下,"Display“将是当前查看的页面。用户可以单击“设置”或“系统设置”,以跳转到相应的页面。
我很想得到关于我的执行情况的反馈。我还想了解更多关于最佳编码实践的知识,以及我的组件中是否有任何强大的代码气味(如果有,为什么会认为它是一种代码气味)。
我认为有些领域可能是代码的气味,但不太确定它们是否是或为什么是这样的:
pathNameIdx--循环中调用while,并在循环退出后再次调用return语句(在我的例子中,只有两个地方)for循环,而不是其他形式,如for..of、for..in或.forEach。import React from "react";
import { useHistory, useLocation } from "react-router-dom";
const RouteBreadcrumbs = ({labels = []}) => {
const location = useLocation();
const history = useHistory();
const pathName = location.pathname;
const numBackslash = (pathName.match(/\//g) || []).length;
if (numBackslash < labels.length) {
return null;
}
let breadcrumbPaths = [];
let pathNameIdx = pathName.length - 1;
for (let i = labels.length - 1; i > 0 ; i--) {
while ((pathName[pathNameIdx] !== "/")) {
console.assert(pathNameIdx >= 0, "pathNameIdx is no longer a valid index");
pathNameIdx--;
}
const newPath = pathName.substring(0, pathNameIdx);
breadcrumbPaths = [newPath, ...breadcrumbPaths];
pathNameIdx--;
}
return (
{labels.map((label, idx) => history.push(breadcrumbPaths[idx]) : null} className={"label2"}>{label})}
)
};
```#qcStackCode#发布于 2020-12-19 05:12:21
你的代码看起来不错。以下是一些你可以尝试的建议:
本部分
{labels.map((label, idx) => history.push(breadcrumbPaths[idx]) : null} className={"label2"}>{label})}是很长的代码行。试着打破它,它使它更容易阅读。
{labels.map((label, idx) => (
history.push(breadcrumbPaths[idx])
: null
}
className={"label2"}
>
{label}
))}(我倾向于使用带有javascript和HTML的2空格缩进,因为它们都倾向于缩进很重,而且在javascript社区中似乎更常见--因此我将在下面的示例中使用这一点)
此外,如果可能的话,不要为元素键使用索引(医生的反应讨论了为什么)。您可以使用breadcrumbPath,因为这对每个元素都是唯一的,并且当路径发生变化时,键将正确地更改。
{labels.map((label, idx) => (
{label}
))}您有一些逻辑来计算URL包含的反斜杠的数量,但是如果您在早期返回时稍等一会儿,您就可以轻松地访问该数字,因为它与breadcrumbsPath.length + 1一样。
if (breadcrumbPaths.length + 1 < labels.length) {
return null;
}在这一点上,当URL上有一个前导斜杠(即'example.com/a/b/')时,这个函数将不能正确地处理这个情况--这需要处理。如果您使用上面的代码片段,那么您只需要在一个地方而不是两个地方修复这个问题--在这里创建breadcrumbPaths数组。
要解决有多个返回的问题:在一个函数中有多个返回没有什么错--实际上,它通常会导致更干净的代码。通常,替代方法意味着较大的if-thens或标志变量,这两个变量都很难理解。我听说,从前,有一个只有一个出口的函数是被鼓励的,但现在已经不是这样了。
现在进入您可能最关心的部分--如何使用c样式的for循环和多个pathNameIdx--s来处理这部分逻辑。
首先,我可能会将逻辑的核心部分分解成一个助手函数。有许多低层次的工作正在进行,阻碍了阅读和理解组件。
下面是您的组件在执行此操作后的处理方法
import React from "react";
import { useHistory, useLocation } from "react-router-dom";
const RouteBreadcrumbs = ({labels = []}) => {
const location = useLocation();
const history = useHistory();
const pathName = location.pathname;
const breadcrumbPaths = ancestorPaths(pathName);
if (breadcrumbPaths.length + 1 < labels.length) {
return null;
}
return (
{labels.map((label, idx) => (
history.push(breadcrumbPaths[idx])
: null
}
className={"label2"}
>
{label}
))}
)
};
function ancestorPaths(pathName) {
let breadcrumbPaths = [];
let pathNameIdx = pathName.length - 1;
while (true) {
while (pathName[pathNameIdx] !== "/") {
if (pathNameIdx === 1) {
return breadcrumbPaths;
}
pathNameIdx--;
}
const newPath = pathName.slice(0, pathNameIdx);
breadcrumbPaths = [newPath, ...breadcrumbPaths];
pathNameIdx--;
}
}注意,在实际的RouteBreadcrumbs组件中,低层次的逻辑是如何发生的?阅读起来要容易得多,并且可以很好地了解正在发生的事情--除了实际的jsx被返回之外--这对我来说有点太嵌套了,但很快就会简化了。
我将在您的原始代码中注意到其他几点:
if (condition) throw new Error(...),或者创建您自己的断言函数,该函数实际上会引发错误。现在让我们看看如何简化我们的新的ancestorPaths()助手函数。可以使用regex收集所有斜杠的指示符,这将与您的工作方式类似,但大大简化了逻辑(请参阅这里)。相反,我只需要拆分'/',构建一个列表,并使用生成器函数,如下所示:
function* iterAncestorPaths(pathName) {
const parts = pathName.split("/").filter(x => x !== "");
for (let i = 1; i < parts.length; ++i) {
yield "/" + parts.slice(0, i).join("/");
}
}请注意,开始时的.filter()将同时处理领先的/和潜在的尾随斜杠。
最后一件事:如果你的面包屑段是链接,为什么不让它们成为实际链接呢?它会简化一些事情。
这是最后的解决方案,还有一些其他的错误调整。
import React from "react";
import { Link, useLocation } from "react-router-dom";
const RouteBreadcrumbs = ({labels = []}) => {
const pathName = useLocation().pathname;
const ancestorPaths = [...iterAncestorPaths(pathName)];
if (ancestorPaths.length + 1 < labels.length) {
return null;
}
return (
{ancestorPaths.map((path, idx) => (
{labels[idx]}
))}
{labels[labels.length - 1]}
);
};
function* iterAncestorPaths(pathName) {
const parts = pathName.split("/").filter(x => x !== "");
for (let i = 1; i < parts.length; ++i) {
yield "/" + parts.slice(0, i).join("/");
}
}https://codereview.stackexchange.com/questions/253443
复制相似问题