收到“<a> cannot appear as a descendant of <a>.” 错误,但不太确定如何构建我的代码以避免此警告

Getting the "<a> cannot appear as a descendant of <a>." error, but not quite sure how to structure my code to avoid this warning

提问人:Sara Dunlop 提问时间:7/21/2023 最后编辑:Sara Dunlop 更新时间:7/21/2023 访问量:126

问:

这发生在我创建的 Reddit 克隆中。如果您使用 Reddit,您可能还记得,当您在提要页面上时,您可以单击帖子以转到该帖子的页面。您还可以单击帖子作者的用户名,然后转到该用户的个人资料页面。

为了实现这一点,我做了以下工作。我的代码实际上并不是这样的结构;我把相关的组件放在一起来展示我在说什么,这是真实事物的大规模浓缩版本:

<NavLink to={`/posts/${post.id}`}>
    <SinglePost>
        <div>
            <SinglePostAuthorBar>
                <div>
                    <NavLink to={`/c/${community?.id}`}>c/{community?.name}</NavLink>
                </div>
            </SinglePostAuthorBar>
        </div>
    </SinglePost>
</NavLink>

正如你所看到的,这导致我把一个标签嵌套在另一个标签里。aa

关于我如何做到这一点的任何建议,这样我就不会违反这个规则,或者你有什么?例如,有没有办法通过单击按钮或其他方式链接到用户的个人资料页面?目前,我不确定我还能如何做到这一点。我需要用户能够点击整个帖子才能链接到帖子的页面,我还需要用户能够点击作者的用户名才能链接到作者的个人资料页面。

谢谢!

编辑:我在这里找到的唯一另一个类似的问题是这个问题,但答案有一个评论者说答案实际上是不好的做法。但是,该问题包含一张图像,该图像显示了本质上导致我遇到的问题的事物的结构。

reactjs react-router-dom 警告

评论


答:

1赞 Asplund 7/21/2023 #1

您可以模拟嵌套链接的点击行为,而不是使用原生标记。显然,除非有必要(在您的情况下),否则不鼓励这样做。<a>

import { useNavigate } from "react-router-dom";

/* ... */

const navigate = useNavigate();
const communityHref = `/c/${community?.id}`;

return (
  <NavLink to={`/posts/${post.id}`}>
    <SinglePost>
      <div>
        <SinglePostAuthorBar>
          <div>
            <span
              onClick={(e) => {
                // prevent parent link from triggering
                e.stopPropagation(); 
                e.preventDefault();
                navigate(communityHref);
              }}
              data-href={communityHref}
              tabIndex={0}
              role="link"
            >
              c/{community?.name}
            </span>
          </div>
        </SinglePostAuthorBar>
      </div>
    </SinglePost>
  </NavLink>
);

评论

0赞 llllvvuu 7/21/2023
nit:我认为如果内部链接是真正的锚元素,而另一个元素是“假”元素,尤其是如果有多个内部链接,那么 a11y 会稍微好一些
0赞 Asplund 7/21/2023
@llllvvuu 我不一定同意,因为父链接和子链接都需要一个事件侦听器,如果父元素是“假的”,则还需要默认阻止子链接以避免触发父链接的事件。您的意见是基于任何来源还是只是偏好?
0赞 Sara Dunlop 7/21/2023
谢谢!我不得不改用,但我明白了它的要点。useHistory()
0赞 llllvvuu 7/21/2023
@Asplund这是出于 a11y 的原因,如果您有一个父元素和 n 个子元素,并且只有父元素是 <a>,那么大多数功能对于禁用用户(也是使用浏览器扩展单击链接的非禁用用户)来说是无法发现的。此外,外部 onClick 行为通常更“可选”,因为用户可以对内部链接执行相同的操作。因此,最好所有内部链接都是可发现的。对于开发人员来说,这可能是一个小小的不便,但用户更重要。.stopPropagation()
0赞 llllvvuu 7/21/2023
@Asplund 就公约的来源而言,Twitter 和 Reddit 都在 <div/>s 中使用了 <a/>s。从语义上讲,帖子是一个块,所以它是有道理的。
0赞 llllvvuu 7/21/2023 #2

tl;dr 将外部 <NavLink /> 更改为 <div onClick={router.push(nextPage)} />

关于Reddit等逆向工程网站的一些提示

在 Reddit 中,该帖子是一个带有处理程序的帖子。您可以在基于 Google Chrome / Chromium 的浏览器中按如下方式进行验证(截至 2023 年 7 月 21 日)。<div>onClick

  1. 右键单击 post -> Inspect Element,然后向上移动,直到找到似乎负责单击处理程序的元素(即包含它的元素不会链接到下一页)。对此有帮助的是右键单击元素并“存储为全局变量” 在 JavaScript 控制台中,然后调用 .temp1.click()
    • 截至 2023 年 7 月 21 日,这是 with(用于 testing-library 或 Puppeteer 之类的东西)。您可以使用“元素”选项卡中的 Ctrl-F 快速跳转到其中之一。<div />data-testid="post-container"
  2. 我们已经有足够的证据表明它是链接,但我们可以更进一步查看“事件侦听器”选项卡,在那里我们可以删除所有点击处理程序并验证不再响应。我们甚至可以弄清楚哪个处理程序专门负责响应。<div /><div />.click().click()

评论

0赞 Sara Dunlop 7/21/2023
据我所知,是Next.js中的一种方法,我没有将其用于此项目。router.push()
0赞 llllvvuu 7/21/2023
无论您使用什么路由框架,您都需要使用等效的功能,React Router、Expo Router 等可能有所不同,但所有路由器都有这样的功能。如果您在问题中指定了 <NavLink /> 来自/您正在使用的路由器,有人可以给出确切的答案,否则您可以搜索它。
0赞 Asplund 7/21/2023
这个答案是人工智能生成的吗?OP没有要求逆向工程的技巧
0赞 llllvvuu 7/21/2023
它不是人工智能生成的。OP 问 Reddit 是怎么做的,我想通了。我实际上不相信 AI 能够做到这一点(目前的 Reddit DOM 可能是在 2021 年之后创建的)
0赞 Asplund 7/21/2023
@llllvvuu明白了,只是检查一下,因为有很多 AI 生成的答案偏离了主题