react-router:如果 <Link> 处于活动状态,如何禁用它?

Posted

技术标签:

【中文标题】react-router:如果 <Link> 处于活动状态,如何禁用它?【英文标题】:react-router: How to disable a <Link>, if its active? 【发布时间】:2016-06-28 01:07:45 【问题描述】:

如果 &lt;Link&gt; 的 URL 已经激活,我如何在 react-router 中禁用它?例如。如果我的 URL 在点击 &lt;Link&gt; 时不会改变,我想完全阻止点击或呈现 &lt;span&gt; 而不是 &lt;Link&gt;

我想到的唯一解决方案是使用activeClassName(或activeStyle)并设置pointer-events: none;,但我更愿意使用适用于IE9和IE10的解决方案。

【问题讨论】:

***.com/a/10276157/1642219 @UDB 渲染 &lt;span&gt; 而不是 &lt;a&gt; 完全没问题。这个问题是特定于 react-router - 不是一般问题。不过谢谢。 【参考方案1】:

我不会问您为什么想要这种行为,但我想您可以将 &lt;Link /&gt; 包装在您自己的自定义链接组件中。

&lt;MyLink to="/foo/bar" linktext="Maybe a link maybe a span" route=this.props.route /&gt;

class MyLink extends Component 
    render () 
        if(this.props.route === this.props.to)
            return <span>this.props.linktext</span>
        
        return <Link to=this.props.to>this.props.linktext</Link>
    

(ES6,但你大概明白了……)

【讨论】:

我明白为什么这很有用。如果 标记被替换为 标记,则当单击链接时,链接将变为面包屑列表中的粗体项。单击另一个面包屑链接将重新启用上一次单击的 标记并将粗体 + 禁用链接状态应用于新单击的链接。 使用 react-router v4,你需要使用this.props.history.location.pathname 而不是this.props.route 我还建议使用this.props.children 而不是添加新的道具linktext。这样,你就可以真正把这个组件当作普通的Link组件来使用了。 无法访问此解决方案。您应该通过aria-disabled="true" 并删除to(尽管Link 要求您通过它)或使用preventDefault 最好使用 react-router 库中的 matchPath 函数来检查路由是否处于活动状态。上述解决方案并非在所有情况下都有效,因此不要依赖 === 比较。【参考方案2】:

如果已经在同一路径上单击,另一种可能性是禁用单击事件。这是一个适用于 react-router v4的解决方案。

import React,  Component  from 'react';
import  Link, withRouter  from 'react-router-dom';

class SafeLink extends Component 
    onClick(event)
        if(this.props.to === this.props.history.location.pathname)
            event.preventDefault();
        

        // Ensure that if we passed another onClick method as props, it will be called too
        if(this.props.onClick)
            this.props.onClick();
        
    

    render() 
        const  children, onClick, ...other  = this.props;
        return <Link onClick=this.onClick.bind(this) ...other>children</Link>
    


export default withRouter(SafeLink);

然后您可以使用您的链接(来自Link 的任何额外道具都可以使用):

<SafeLink className="some_class" to="/some_route">Link text</SafeLink>

【讨论】:

【参考方案3】:

所有 React Router NavLink 的优点与 disable 能力。

import React from "react"; // v16.3.2
import  withRouter, NavLink  from "react-router-dom"; // v4.2.2

export const Link = withRouter(function Link(props) 
  const  children, history, to, staticContext, ...rest  = props;
  return <>
    history.location.pathname === to ?
      <span>children</span>
      :
      <NavLink ...to, ...rest>children</NavLink>
    
  </>
);

【讨论】:

props 没有属性 to 也没有字符串索引签名。 我不确定我是否理解您的评论,但请注意 const Link... 是一个 HOC。道具也是withRouter给定道具和通常的Link道具的合并。 to 属于包裹 Link 的道具。 PS:NavLinkLink 的特殊版本,因此也带有to 属性。 reacttraining.com/react-router/web/api/NavLink 抱歉,“类型化”的解决方案超出了范围,我更愿意提供一个更有针对性的解决方案。但请随意提供带有您选择的方言的打字版本。【参考方案4】:

您可以使用 CSS 的 pointer-events 属性。这适用于大多数浏览器。比如你的JS代码:

class Foo extends React.Component 
  render() 
    return (
      <Link to='/bar' className='disabled-link'>Bar</Link>
    );
  

和 CSS:

.disabled-link 
  pointer-events: none;

链接:

pointer-events CSS property How to disable html links

随附的How to disable HTML links 答案建议同时使用disabledpointer-events: none 以获得最大的浏览器支持。

a[disabled] 
    pointer-events: none;

来源链接:How to disable Link

【讨论】:

请务必注意pointer-events 技术只会禁用来自指针设备的点击。仍然可以使用键盘选择和激活链接。【参考方案5】:

这对我有用:

<Link to=isActive ? '/link-to-route' : '#' />

【讨论】:

isActive 来自哪里? 它来自 props/state。【参考方案6】:

React Router 的 Route 组件具有 three different ways 以根据当前路由呈现内容。虽然component 最常用于仅在匹配期间显示组件,但children 组件接受(match) =&gt; return &lt;stuff/&gt; 回调,可以在匹配时渲染事物即使路由不匹配.

我创建了一个 NavLink 类,它用 span 替换 Link 并在其 to 路由处于活动状态时添加一个类。

class NavLink extends Component 
  render() 
    var  className, activeClassName, to, exact, ...rest  = this.props;
    return(
      <Route
        path=to
        exact=exact
        children=( match ) => 
          if (match) 
            return <span className=className + " " + activeClassName>this.props.children</span>;
           else 
            return <Link className=className to=to ...rest/>;
          
        
      />
    );
  

然后像这样创建一个导航链接

<NavLink to="/dashboard" className="navlink" activeClassName="active">

React Router's NavLink 做了类似的事情,但仍然允许用户点击链接并推送历史记录。

【讨论】:

【参考方案7】:

基于 nbeuchat 的回答和组件 - 我创建了一个自己改进的组件版本,它为我的项目覆盖 react router's Link 组件。

在我的情况下,我必须允许将对象传递给 to 道具(就像本机 react-router-dom 链接所做的那样),我还添加了对 search queryhash 的检查以及 pathname

import PropTypes from 'prop-types';
import React,  Component  from 'react';
import  Link as ReactLink  from 'react-router-dom';
import  withRouter  from "react-router";

const propTypes = 
  to: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
  location: PropTypes.object,
  children: PropTypes.node,
  onClick: PropTypes.func,
  disabled: PropTypes.bool,
  staticContext: PropTypes.object
;

class Link extends Component 
  handleClick = (event) => 
    if (this.props.disabled) 
      event.preventDefault();
    

    if (typeof this.props.to === 'object') 
      let 
        pathname,
        search = '',
        hash = ''
       = this.props.to;
      let  location  = this.props;

      // Prepend with ? to match props.location.search
      if (search[0] !== '?') 
        search = '?' + search;
      

      if (
        pathname === location.pathname
        && search === location.search
        && hash === location.hash
      ) 
        event.preventDefault();
      
     else 
      let  to, location  = this.props;

      if (to === location.pathname + location.search + location.hash) 
        event.preventDefault();
      
    

    // Ensure that if we passed another onClick method as props, it will be called too
    if (this.props.onClick) 
      this.props.onClick(event);
    
  ;

  render() 
    let  onClick, children, staticContext, ...restProps  = this.props;
    return (
      <ReactLink
        onClick= this.handleClick 
         ...restProps 
      >
         children 
      </ReactLink>
    );
  


Link.propTypes = propTypes;

export default withRouter(Link);

【讨论】:

【参考方案8】:

如果它适合您的设计,则在其上放置一个 div,然后操作 z-index。

【讨论】:

【参考方案9】:

解决此问题的另一种选择是使用ConditionalWrapper 组件,该组件根据条件呈现&lt;Link&gt; 标记。

这是我在此处基于此博客使用的ConditionalWrapper 组件 https://blog.hackages.io/conditionally-wrap-an-element-in-react-a8b9a47fab2:

const ConditionalWrapper = ( condition, wrapper, children ) =>
    condition ? wrapper(children) : children;

export default ConditionalWrapper

这就是我们使用它的方式:

const SearchButton = () => 
    const 
        searchData,
     = useContext(SearchContext)

    const isValid = () => searchData?.search.length > 2

    return (<ConditionalWrapper condition=isValid()
                                wrapper=children => <Link href=buildUrl(searchData)>children</Link>>
            <a
                className=`ml-auto bg-$isValid()
                    ? 'primary'
                    : 'secondary' text-white font-filosofia italic text-lg md:text-2xl px-4 md:px-8 pb-1.5`>t(
                    'search')</a>
        </ConditionalWrapper>
    )

此解决方案不呈现 Link 元素,并且还避免了代码重复。

【讨论】:

以上是关于react-router:如果 <Link> 处于活动状态,如何禁用它?的主要内容,如果未能解决你的问题,请参考以下文章

React-router <Link> 没有活动类

React-router:使用 <Link> 作为可点击的数据表行

强制 React-Router <Link> 加载页面,即使我们已经在该页面上

[react-router] React-Router的<Link>标签和<a>标签有什么区别

在将 react-router 5 与 redux 7 一起使用时,react-router <Link> 在转到新路由后不会重置状态

通过 React-Router v4 <Link /> 传递值