【问题标题】:react-router: How to disable a <Link>, if its active?react-router:如果 <Link> 处于活动状态,如何禁用它?
【发布时间】: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的解决方案。

【问题讨论】:

标签: javascript reactjs 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;
    }
    

    链接:

    随附的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 元素,并且还避免了代码重复。

          【讨论】:

            猜你喜欢
            • 2013-10-12
            • 2022-06-28
            • 1970-01-01
            • 1970-01-01
            • 2017-05-26
            • 2017-01-04
            • 2016-07-17
            • 2021-11-05
            相关资源
            最近更新 更多