You should not use <Link> outside a <Router>

I'm trying to set up react-router in an example application, and I'm getting the following error:

You should not use <Link> outside a <Router>

My app is set up like so:

Parent component

const router = (
<div className="sans-serif">
<Router histpry={browserHistory}>
<Route path="/" component={Main}>
<IndexRoute component={PhotoGrid}></IndexRoute>
<Route path="/view/:postId" component={Single}></Route>
</Route>
</Router>
</div>
);


render(<Main />, document.getElementById('root'));

Child/Main component

export default () => (
<div>
<h1>
<Link to="/">Redux example</Link>
</h1>
</div>
)

Any idea what I'm doing wrong here?

Here's a Sandbox link to demonstrate the problem.

171226 次浏览

我假设您正在使用 React-RouterV4,就像您在原来的 沙盒连结。中使用的那样

您在对 ReactDOM.render的调用中呈现 Main组件,该调用呈现 Link,而 Main 组件在 Router之外,这就是它抛出错误的原因:

不应在 < 路由器 > 外使用 < Link >

变化 :

  1. Use any one of these Routers, BrowserRouter/HashRouter etc..., because you are using React-Router V4.

  2. Router can have only one child, so wrap all the routes in a div or 换人.

  3. React-Router V4没有嵌套路由的概念,如果您想使用嵌套路由,那么可以直接在组件内部定义这些路由。


检查每个更改的 < a href = “ https://codesandbox.io/s/qvv3qo5vyw”rel = “ noReferrer”> 本工作示例

家长组成部分:

const App = () => (
<BrowserRouter>
<div className="sans-serif">
<Route path="/" component={Main} />
<Route path="/view/:postId" component={Single} />
</div>
</BrowserRouter>
);


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

来自路线的 Main组件

import React from 'react';
import { Link } from 'react-router-dom';


export default () => (
<div>
<h1>
<Link to="/">Redux example</Link>
</h1>
</div>
)

等等。


还要检查这个答案: 使用反应路由器 v4的嵌套路由

我想出了这个代码:

import React from 'react';
import { render } from 'react-dom';


// import componentns
import Main from './components/Main';
import PhotoGrid from './components/PhotoGrid';
import Single from './components/Single';


// import react router
import { Router, Route, IndexRoute, BrowserRouter, browserHistory} from 'react-router-dom'


class MainComponent extends React.Component {
render() {
return (
<div>
<BrowserRouter history={browserHistory}>
<Route path="/" component={Main} >
<IndexRoute component={PhotoGrid}></IndexRoute>
<Route path="/view/:postId" component={Single}></Route>
</Route>
</BrowserRouter>
</div>
);
}
}


render(<MainComponent />, document.getElementById('root'));

我认为错误是因为您呈现的是 Main组件,而 Main组件对 Router一无所知,所以您必须呈现它的父组件。

如果您不想做太多的更改,可以在 onClick ()方法中使用下面的代码。

this.props.history.push('/');

呈现中编写 路由器代替 Main(代码的最后一行)。 像这样 ReactDOM.render (路由器,document.getElementById (‘ root’)) ;

将 Link 组件包含在 BrowserRouter 组件中

 export default () => (
<div>
<h1>
<BrowserRouter>
<Link to="/">Redux example</Link>
</BrowserRouter>
</h1>
</div>
)

每当您尝试在 BrowserRouter之外的页面上显示 Link时,都会得到这个错误。

这个错误消息实际上是说,任何不是我们 <Router>的子组件都不能包含任何与 React 路由器相关的组件。

您需要将您的组件层次结构迁移到您在上面第一个答案中看到的层次结构。对于其他评论这篇文章的人来说,他们可能需要看到更多的例子。

Let's say you have a Header.jscomponent that looks like this:

import React from 'react';
import { Link } from 'react-router-dom';


const Header = () => {
return (
<div className="ui secondary pointing menu">
<Link to="/" className="item">
Streamy
</Link>
<div className="right menu">
<Link to="/" className="item">
All Streams
</Link>
</div>
</div>
);
};


export default Header;

你的 App.js文件是这样的:

import React from 'react';
import { BrowserRouter, Route, Link } from 'react-router-dom';
import StreamCreate from './streams/StreamCreate';
import StreamEdit from './streams/StreamEdit';
import StreamDelete from './streams/StreamDelete';
import StreamList from './streams/StreamList';
import StreamShow from './streams/StreamShow';
import Header from './Header';


const App = () => {
return (
<div className="ui container">
<Header />
<BrowserRouter>
<div>
<Route path="/" exact component={StreamList} />
<Route path="/streams/new" exact component={StreamCreate} />
<Route path="/streams/edit" exact component={StreamEdit} />
<Route path="/streams/delete" exact component={StreamDelete} />
<Route path="/streams/show" exact component={StreamShow} />
</div>
</BrowserRouter>
</div>
);
};


export default App;

Notice that the Header.js component is making use of the Link tag from react-router-dom but the componet was placed outside the <BrowserRouter>, this will lead to the same error as the one experience by the OP. In this case, you can make the correction in one move:

import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import StreamCreate from './streams/StreamCreate';
import StreamEdit from './streams/StreamEdit';
import StreamDelete from './streams/StreamDelete';
import StreamList from './streams/StreamList';
import StreamShow from './streams/StreamShow';
import Header from './Header';


const App = () => {
return (
<div className="ui container">
<BrowserRouter>
<div>
<Header />
<Route path="/" exact component={StreamList} />
<Route path="/streams/new" exact component={StreamCreate} />
<Route path="/streams/edit" exact component={StreamEdit} />
<Route path="/streams/delete" exact component={StreamDelete} />
<Route path="/streams/show" exact component={StreamShow} />
</div>
</BrowserRouter>
</div>
);
};


export default App;

Please review carefully and ensure you have the <Header /> or whatever your component may be inside of not only the <BrowserRouter> but also inside of the <div>, otherwise you will also get the error that a Router may only have one child which is referring to the <div> which is the child of <BrowserRouter>. Everything else such as Route and components must go within it in the hierarchy.

因此,现在 <Header /><div>标记中 <BrowserRouter>的子元素,它可以成功地利用 Link元素。

For 开玩笑的 users

如果您使用 Jest 进行测试,并且发生此错误,那么只需用 <BrowserRouter>包装您的组件即可

describe('Test suits for MyComponentWithLink', () => {
it('should match with snapshot', () => {
const tree = renderer
.create(
<BrowserRouter>
<MyComponentWithLink/>
</BrowserRouter>
)
.toJSON();
expect(tree).toMatchSnapshot();
});
});

简单点说:

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

别忘了: import { BrowserRouter } from "react-router-dom";

之所以出现这个错误,是因为我从 npm 库导入了一个可重用的组件,而 react-router-dom的版本不匹配。

因此,请确保在两个地方使用相同的版本!

你可以把链接组件放在路由器组件中,比如:

 <Router>
<Route path='/complete-profiles' component={Profiles} />
<Link to='/complete-profiles'>
<div>Completed Profiles</div>
</Link>
</Router>

I base my answer on this article. Here's a simple solution.

// index.test.tsx
import React from "react";
import { render, screen } from "@testing-library/react";
import { Router } from "react-router-dom";
import { createBrowserHistory } from "history";
import Login from ".";


it("should display Login", () => {
const history = createBrowserHistory();
render(
<Router history={history}>
<Login />
</Router>
);
const linkElement = screen.getByText(/Login/i);
expect(linkElement).toBeInTheDocument();
});

为什么不尝试创建另一个组件,它只使用来自“ response-router-dom”的 BrowserRouter,然后将您的组件包装在其中的 Link 中。

const ReduxExample () => (
<div>
<h1>
<Link to="/">Redux example</Link>
</h1>
</div>
)

你就能做到

const NewReduxExample = () => {
<BrowserRouter>
<ReduxExample />
</BrowserRouter>
};

下面是我的解决方案,我使用 create-response-app 附带的反应测试库。我知道这是一个简单的解决方案,但是我忽略了其他答案的导入,所以我只是把它放在一起。 下面是测试文件:

// HomePage.test.js
import { render, screen } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import HomePage from './HomePage';


it('Homepage contains heading 1', () => {
render(
<BrowserRouter>
<HomePage />
</BrowserRouter>
);


screen.getByRole('heading', { level: 1 });
});


it('Homepage contains link', () => {
render(
<BrowserRouter>
<HomePage />
</BrowserRouter>
);


expect(screen.getByRole('link')).toHaveAttribute('href', '/articles');
});

The main component that I would be testing would look like this

// HomePage.js
import React from 'react';
import { Link } from 'react-router-dom';


const HomePage = () => {
return (
<>
<h1>Welcome to React page</h1>
<p>
Click on <Link to='/articles'>Articles</Link> to see what is shared
today
</p>
</>
);
};


export default HomePage;

或者,您可以在 src 文件夹中的 index.js 文件中执行此操作。

import React from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter as Router } from 'react-router-dom';

import App from './App';

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

然后,在 Component. jsx 文件中可以导入 Link

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

并且像这样不使用路由器/路由标签来使用链接。

<Link to="/">Page</Link>

wrap every component inside BrowserRouter where you're calling reactDom.render like this

import {BrowserRouter} from 'react-router-dom'


ReactDOM.render(


<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);

把它添加到 index.js:

import { BrowserRouter as Router } from "react-router-dom";

然后在 <Router></Router>中包装您的 <App />,以便在您的反应应用程序中使用链接。

像这样:

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router } from "react-router-dom";


import App from "./App";


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