代码之家  ›  专栏  ›  技术社区  ›  Krimson

将React路由器主动导航链接与子组件集成

  •  6
  • Krimson  · 技术社区  · 6 年前

    我的菜单当前如下所示:

    <Menu>
        <MenuItem text="link1" />
        <MenuItem text="link2" />
    </Menu>
    

    与集成 react-router-dom ,我按以下方式修改:

    <Menu>
    
        <NavLink to="link1">
            <MenuItem text="link1" />
        </NavLink>
    
        <NavLink to="link2">
            <MenuItem text="link2" />
        </NavLink>
    
    </Menu>
    

    我的 MenuItem 组件支持当前“活动”链接的样式,如下所示:

    <MenuItem active text="link1" />
    

    如何将“主动”路线样式与 反应路由器dom ?

    是否可以用以下方式设置布尔值?

    <MenuItem active={isRouteActive} text="link1" />
    

    编辑: MenuItem 是Blueprintjs中的组件。当 active 如果设置了道具,则可能会将一些内部样式规则应用于组件。我可以用 activeClassName 支柱打开 NavLink 但我不想复制第三方组件的css。

    6 回复  |  直到 6 年前
        1
  •  9
  •   Krimson    6 年前

    我最终找到了答案。我使用 Route 组成部分

    const MenuItemExt = ({ icon, text, to, activeOnlyWhenExact }) => {
      return (
        <Route
          path={to}
          exact={activeOnlyWhenExact}
          children={({match, history}) => (
            <MenuItem
              active={match}
              icon={icon}
              onClick={() => {
                history.push(to);
              }}
              text={text}
            />
          )}
        />
      );
    };
    
        2
  •  5
  •   Hsien lxyyz    5 年前

    不幸的是,来自rr4的NavLink没有提供通过的方法 isActive 暂时将信息发送给其子项(MenuItem)。

    好消息是,rr4采用了以组件为中心的方法,因此实际上一切都只是反应组件,NavLink本身就是一个由以下组件组成的自定义组件:

    • <Route/> (处理路由匹配)
    • <Link/> (显示路由操作)

    我们可以创建自定义链接排序的版本,如:

    const NavLink = ({
      to,
      children,
      className,
      activeClassName,
      ...rest
    }) => {
      const path = typeof to === "object" ? to.pathname : to;
      return (
        <Route
          path={path}
          children={({ match }) => {
            const isActive = !!match;
            return (
              <Link
                {...rest}
                className={
                  isActive
                   ? [className, activeClassName].filter(i => i).join(" ")
                   : className
                }
                to={to}
              >
                {typeof children === 'function' ? children(isActive) : children}
              </Link>
            );
          }}
        />
      );
    };
    
    export default NavLink;
    

    然后将其用作:

    <Menu>
      <NavLink to="link1">
        {isActive => <MenuItem active={isActive} text="link1" />}
      </NavLink>
      <NavLink to="link2">
        {isActive => <MenuItem active={isActive} text="link2" />}
      </NavLink>
    </Menu>
    

    这使用 render props 从父级到子级共享isActive状态。这应该可以正常工作,但要完全模仿rr4中NavLink的工作方式(例如,可访问性支持),请根据其 implementation 可能仍然需要。

    对于这种特定情况,使用带有链接的本地状态可能更容易

    state = { activeLink: '' }
    render() {
      return (
        <Menu>
          <Link to='link1' onClick={() => this.setState({ activeLink: 'link1' })>
            <MenuItem active={this.state.activeLink === 'link1'} text='link1' />
          </Link>
          <Link to='link2' onClick={() => this.setState({ activeLink: 'link2' })>
            <MenuItem active={this.state.activeLink === 'link2'} text='link2' />
          </Link>
        </Menu>
      )
    }
    
        3
  •  1
  •   Sebastian Pikand    4 年前

    假设您有两条路线:

    /管理员

    /管理员/权限

    当以上任一链接打开时,您希望导航栏文本“Admin”为活动颜色。您可以通过以下方式实现这一点:

    <NavLink
                exact
                to="/admin"
                className="..."
                activeClassName="..."
                isActive={(match, location) => {
                  let pathStrings = location.pathname.split('/');
    
    // pathstrings will be ['/', 'admin', '/', 'privileges']
    // should you add a new subroute, say /admin/locations/ the code would still work
    
    
                  if (match) {
                    return true;
                  } else if (pathStrings[1] === 'admin') {
                    return true;
                  } else {
                    return false;
                  }
                }}
              >
                Admin
              </NavLink>
    
        4
  •  0
  •   Giang Le    6 年前

    你可以使用道具 activeClassName 来自react路由器。

    <NavLink activeClassName="custom-active-class" to="link1">
        <MenuItem className="custom-link-class" text="link1" />
    </NavLink>
    
    <NavLink activeClassName="custom-active-class" to="link2">
        <MenuItem className="custom-link-class" text="link2" />
    </NavLink>
    

    并稍微更改css

    .custom-active-class .custom-link-class{ background-color: red; }

        5
  •  0
  •   Abdul Sadik Yalcin    3 年前

    这是另一种方法;如果您正在一组导航链接上循环,需要进行动态检查;

    // Routes
    <Route exact path="/news" component={News} />
    <Route path="/news/:nid" component={NewsDetails} />
    
    {this.state.navItems.auth.map((item, index) => {
      return (
        <li key={index} className="nav-item">
          <NavLink
            exact
            to={item.path}
            className="nav-link"
            activeClassName="active"
            onClick={this.handleClick}
            isActive={(match, location) => {
              if (match) {
                return true;
              }
    
              if (location.pathname.indexOf(item.path) > -1) {
                // Prevent home from matching
                if (item.path !== '/') {
                  return true;
                }
              }
            }}>
            ...
          </NavLink>
        </li>
      );
    })}
    
        6
  •  0
  •   Jar    2 年前

    这里有很多很棒的答案。下面是我根据这些建议最终得出的结论,还通过一个非常酷的数组添加了多个路径。您认为至少需要v4才能做到这一点

        <Route
                    path={['/app/discover', '/app/discover', '/app/engage']}
                    exact={false}
                    children={({ match, history }) => (
                        <Tab
                            handleClick={() => {
                                if (userCan(perms.TRENDS_READ)) {
                                    history.push(routes.app.discover.trends.path)
                                } else {
                                    history.push(routes.app.engage.lists.lists.path)
                                }
                            }}
                            tabImage={
                                <OutcomeImage
                                    className={'opacityOneHover'}
                                    fill='white'
                                    opacity={match ? 1 : 0.5}
                                />
                            }
                        />
                    )}
                />