ReactJS Bootstrap导航栏和路由无法同时使用

73

我正在尝试使用ReactJS创建一个简单的Webapp,我想使用React-Bootstrap提供的 Navbar 。我创建了一个名为 Navigation.js 的文件,其中包含一个名为 Navigation 的类,以将 Navbar 和路由从 App.js 文件中分离。然而,这两个部分似乎都没有起作用。当我加载页面时,页面是空白的,没有Navbar。有人能找出错误吗?

Navigation.js:

import React, { Component } from 'react';
import { Navbar, Nav, Form, FormControl, Button, NavItem } from 'react-bootstrap';
import { Switch, Route } from 'react-router-dom';
import { Home } from './Page';

class Navigation extends Component {
    render() {
        return (
            <div>
                <div>
                    <Navbar>
                        <Navbar.Brand href="/">React-Bootstrap</Navbar.Brand>
                        <Navbar.Collapse>
                            <Nav className="mr-auto">
                                <NavItem eventkey={1} href="/">
                                    <Nav.Link href="/">Home</Nav.Link>
                                </NavItem>
                            </Nav>
                            <Form inline>
                                <FormControl type="text" placeholder="Search" className="mr-sm-2" />
                                <Button variant="outline-success">Search</Button>
                            </Form>
                        </Navbar.Collapse>
                    </Navbar>
                </div>
                <div>
                    <Switch>
                        <Route exact path='/' component={Home} />
                        <Route render={function () {
                            return <p>Not found</p>
                        }} />
                    </Switch>
                </div>
            </div>
        );
    }
}

export default Navigation;

App.js:

import React, { Component } from 'react';
import Navigation from './components/routing/Navigation';



class App extends Component {
  render() {
    return (
      <div id="App">
        <Navigation />
      </div>
    );
  }
}

export default App;
我尝试使用来自`react-router-bootstrap`的`LinkContainer`作为包含在`NavItem`中的内容,但结果相同。 为了完整起见,这是Page.js的代码:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';

export const Page = ({ title }) => (
    <div className="App">
      <div className="App-header">
        <h2>{title}</h2>
      </div>
      <p className="App-intro">
        This is the {title} page.
      </p>
      <p>
        <Link to="/">Home</Link>
      </p>
      <p>
        <Link to="/about">About</Link>
      </p>
      <p>
        <Link to="/settings">Settings</Link>
      </p>
    </div>
);


export const About = (props) => (
    <Page title="About"/>
);

export  const Settings = (props) => (
    <Page title="Settings"/>
);

export const Home = (props) => (
    <Page title="Home"/>
);
7个回答

230
首先,在你的代码片段中,似乎没有将你的代码包装在一个Router中,所以你应该确保在App内部或者在ReactDOM.render中这样做:
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>, 
  rootElement
  );

接下来,你的具体问题是你正在渲染React-Bootstrap的Nav.Link组件,而不是React-Router的Link组件,所以路由器没有捕获到你的路由变化。幸运的是,React-Bootstrap在大多数组件中提供了一个渲染属性,可以指定你希望渲染哪个组件或元素,如果你不想使用默认的组件或元素。尝试切换成以下内容:

import { Switch, Route, Link } from 'react-router-dom';

class Navigation extends Component {
  render() {
    return (
      <div>
        <div>
          <Navbar>
            <Navbar.Brand as={Link} to="/" >React-Bootstrap</Navbar.Brand>
            <Navbar.Collapse>
              <Nav className="mr-auto">
                <NavItem eventkey={1} href="/">
                  <Nav.Link as={Link} to="/" >Home</Nav.Link>
                </NavItem>
              </Nav>
              <Form inline>
                <FormControl type="text" placeholder="Search" className="mr-sm-2" />
                <Button variant="outline-success">Search</Button>
              </Form>
            </Navbar.Collapse>
          </Navbar>
        </div>
        <div>
          <Switch>
            <Route exact path='/' component={Home} />
            <Route render={function () {
              return <p>Not found</p>
            }} />
          </Switch>
        </div>
      </div>
    );
  }
}

14
谢谢您先生!as={Link}和to"..."是我所缺失的。奇怪的是,Bootstrap文档根本没有提到这一点。 - mBrice1024
看起来应该使用 Nav.Item 而不是 NavItem - kugo2006
1
@kugo2006 他们是同一个组件,但是使用 Nav.Item 可以少导入一个组件。此外,这个问题是关于 Nav.Link 和路由器的,看起来我只是复制了 OP 的代码片段并进行了修复,所以我没有进行太多重构 :) - SrThompson
我想知道在文档中哪里可以找到那个。请有人贴出链接,以便参考。我们怎么知道有'as'属性? - Georgi Georgiev
2
@GeorgiGeorgiev https://react-bootstrap.github.io/components/navs/#nav-link-props - SrThompson
这个修复引入的唯一问题是,当您单击链接时,导航栏不再折叠(即使在导航栏上存在collapseOnSelect)。 - nxasdf

42

如果在react-bootstrap导航栏中使用react-router-domLink组件时存在样式问题,只需添加className="nav-link"即可,如下所示:

<Link to="/" className="nav-link">Home</Link>

而不是:

<Nav.Link href="/">Home</Nav.Link>


10
我也碰到了同样的问题,但我认为最好使用以下代码:<Nav.Link as={Link} href="/">Home</Nav.Link>其中{Link}react-router-dom组件。 - Matías Pizarro
5
如果使用 as={Link},则必须将 href 替换为 to - Max
2
谢谢。这些答案正是我在寻找的内容 <Link to="/" className="nav-link">Home</Link> 或者 <Nav.Link as={Link} to="/" >Home</Nav.Link> - K. Shaikh

12
希望我来得及帮助其他人解决这个问题。
你可以使用NavLink,而不是as={NavLink}。它会以Link相同的行为进行渲染,但会“监视”路由URL以确定哪个链接实际上是活动的。
<Nav defaultActiveKey="/bills" as="ul">
      <Nav.Item as="li">
        <Nav.Link as={NavLink} to="/bills">Dividas</Nav.Link>
      </Nav.Item>
      <Nav.Item as="li">
        <Nav.Link as={NavLink} to="/other">Em construção</Nav.Link>
      </Nav.Item>
    </Nav>

这个不起作用:类型 '"{Link}"' 不能赋值给类型 'ElementType<any> | undefined'。你是想说 '"link"' 吗? - Richard Barraclough
2
导入 { NavLink } from 'react-router-dom',这就是你想要使用的 NavLink - Angel S. Moreno

2

目前,使用react v18和react-router v6.4时,我使用的方法有些不同。

要使链接与react-route正常工作,请在Nav.Link项中使用as={Link}

为了使选项卡高亮显示,您需要使用eventKey,如文档中所述EventKey用于确定和控制父Nav的活动状态

这是一个例子。

请注意,更改还会影响Navbar.Brand组件。

import { Link, useLocation } from 'react-router-dom';

const AppNavbar = () => {
  const location = useLocation();

  const activeKey = location.pathname === '/' ? '/projects' : location.pathname;

  return (
    <Navbar expand="lg" className="theme-navbar">
      <Container>
        <Navbar.Brand as={Link} to="/">
          My Projects
        </Navbar.Brand>
        <Navbar.Toggle aria-controls="basic-navbar-nav" />
        <Navbar.Collapse id="basic-navbar-nav">
          <Nav activeKey={activeKey} className="me-auto">
            <Nav.Link as={Link} eventKey="/projects" to="/projects">
              Projects
            </Nav.Link>
            <Nav.Link as={Link} eventKey="/work" to="/work">
              Ongoing Tasks
            </Nav.Link>
          </Nav>
        </Navbar.Collapse>
      </Container>
    </Navbar>
  );
};

1
太好了!谢谢。这些API变化得如此之快,其他所有内容都已过时。 - Trees4theForest
类型“{Link}”不能赋值给类型“ElementType<any> | undefined”。你是不是想用“link”? - Richard Barraclough

2
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);

以及 Navbar.js:

import React from 'react';
import {Container,Navbar,Nav,NavItem } from 'react-bootstrap';
import {Link} from 'react-router-dom';

<Nav className="ml-auto">
<NavItem>   <Link className="nav-link"   to="/">Home</Link> </NavItem> 
<NavItem>   <Link  className="nav-link" to="/about">About</Link> </NavItem> 
<NavItem>    <Link className="nav-link"   to="/contact">Contact</Link> </NavItem> 
</Nav>

这对我解决了一个问题:在<Router>之外使用<Link>时出现的错误。

1
我发现自己有一个相当复杂的项目,而且已经依赖于jQuery,我能够通过在document上添加事件监听器来优雅地解决react-router / react-bootstrap不匹配的问题。
这种方法有一个优点,即不需要对当前标记进行任何更改。然而,根据需要,可能需要编写一些额外的逻辑来保护history.push调用。还可能需要扩展此内容以保护target属性,例如target="_blank"
如果不需要使用jQuery,则可以轻松编写基于vanilla JS的实现,使用document.addEventListener即可。
// react-router@5
// usage of history may vary depending on version
import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';

const history = createBrowserHistory();

// IIFE scoping jQuery for us
(($) => {
  // Wait for document ready
  $(() => {
    $(document).on('click', '[href]', (event) => {
      // Only target links targeting our application's origin
      if (event.currentTarget.href.indexOf(window.location.origin) === 0) {
        // Prevent standard browser navigation
        event.preventDefault();

        const path = event.currentTarget.href.slice(window.location.origin.length);

        history.push(path);
      }
    });
  });
})(jQuery);

const Routing = (props) => (
  <Router history={ history }>
    ...
  </Router>
);

-4

我认为您忘记了包含Bootstrap CSS,请参考以下文档中的样式表部分。

https://react-bootstrap.github.io/getting-started/introduction/

或者只需将以下内容添加到您的index.html中

<link
  rel="stylesheet"
  href="https://maxcdn.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
  integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
  crossorigin="anonymous"
/>

我已经在index.js中包含了bootstrap css,无论如何还是感谢你的回答。 - NotAName
1
这与CSS无关,而是涉及React Router。 - Bender

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接