React Router Error: You should not use Route outside of Router - javascript

I'm just doing some basic routing in my react app and I've done it this way before so I'm pretty confused to as why it isn't working now.
The error I am getting says: You should not use <Route> or withRouter() outside a <Router>
I'm sure this is super basic so thanks for baring with me!
import React from 'react'
import { Route } from 'react-router-dom'
import { Link } from 'react-router-dom'
import * as BooksAPI from './BooksAPI'
import BookList from './BookList'
import './App.css'
class BooksApp extends React.Component {
state = {
books: []
}
componentDidMount() {
this.getBooks()
}
getBooks = () => {
BooksAPI.getAll().then(data => {
this.setState({
books: data
})
})
}
render() {
return (
<div className="App">
<Route exact path="/" render={() => (
<BookList
books={this.state.books}
/>
)}/>
</div>
)
}
}
export default BooksApp

You need to setup context provider for react-router
import { BrowserRouter, Route, Link } from 'react-router-dom';
// ....
render() {
return (
<BrowserRouter>
<div className="App">
<Route exact path="/" render={() => (
<BookList
books={this.state.books}
/>
)}/>
</div>
</BrowserRouter>
)
}
Side note - BrowserRouter should be placed at the top level of your application and have only a single child.

I was facing the exact same issue. Turns out that i didn't wrap the App inside BrowserRouter before using the Route in App.js.
Here is how i fixed in index.js.
import {BrowserRouter} from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>
document.getElementById('root')
);

Related

React-router is not displaying component in the browser

Inside App.js:
import React, { useState } from 'react';
import './App.css';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Dashboard from './components/Dashboard/Dashboard';
import Preferences from './components/Preferences/Preferences';
import Login from './components/Login/Login';
function App() {
const [token, setToken] = useState();
if(!token) {
return <Login setToken={setToken} />
}
return (
<div className="wrapper">
<h1>Application</h1>
<BrowserRouter>
<Routes>
/*<Route path="/dashboard">*/
<Route path="/dashboard" element={<Dashboard/>} /></Route>
/*<Route path="/preferences">*/
<Route path="/preferences" element={<Preferences/>} /></Route>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;`
Inside Dashboard.js (../src/components/Dashboard/Dashboard.js):
import React from 'react';
export default function Dashboard() {
return(
<h2>Dashboard</h2>
);
}
Url: http://localhost:3000/dashboard
I want to see the Dashboard content along with the App page content (Application and Dashboard headers) when I load the browser. But when I load the browser, it only displays the App page content and getting the same error:
"Matched leaf route at location "/dashboard" does not have an element. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page."
You are using Routes instead of Router. Replace it on your line 3 and in the return().
Source: React-router https://v5.reactrouter.com/web/api/Route
//...
import { BrowserRouter, Route, Router } from 'react-router-dom';
//...
return ( ...
<Router>
/*<Route path="/dashboard">*/
<Route path="/dashboard" element={<Dashboard/>} />
/*<Route path="/preferences">*/
<Route path="/preferences" element={<Preferences/>} />
</Router>
...)
export default App;
Please specify which version of React router you are using, since a lot of the functionality has changed, is it 6.4 or is still 5 ?
Either way, please remove the comments of the routes, I don't think they help at all.
if you have chosen BrowserRouter from the 6.4 version then it should be used like this
import { BrowserRouter, Route } from 'react-router-dom';
return (
<BrowserRouter>
<Route path="/" element={<RootComp />} >
<Route path="dashboard" element={<Dashboard/>} />
<Route path="preferences" element={<Preferences/>} />
</Route>
</BrowserRouter>
)
export default App;
Where <RootComp /> should have an <Outlet /> as children
import { Outlet } from 'react-router-dom';
const RootComp = () => {
return <div><Outlet /></div>
}
export default RootComp;
Again, this is for the latest React Router component, however, I would advise using createBrowserRouter() rather than the old component-based trees, this way you can programatically create and manage the routes in an Object.

Why is React Router v6 not rendering my components?

I know this question has been asked a lot and I've looked at quite a lot of answers and articles including one on how to upgrade from React Router V5 to V6 since I'm used to V5. However, V6 is giving me a weird issue which I don't know how to fix or what am I doing wrong.
Here's my code below
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Login from './components/dashboard/Login';
import Profile from './components/dashboard/Profile';
import './resources/style/tutorApp.css';
export default class App extends Component {
static displayName = App.name;
render () {
return (
<Router>
<Routes>
<Route path="/" element={ <Profile /> } />
<Route path="/Login" element={ <Login /> } />
</Routes>
</Router>
);
}
}
Profile.js
import React, { Component } from 'react';
class Profile extends Component {
render() {
return (
<div>
<h1>This is my Profile Page.</h1>
</div>
);
}
}
export default Profile;
Login.js
import React, { Component } from 'react';
class Login extends Component {
render() {
return (
<div>
<h1>This is my Login page.</h1>
</div>
);
}
}
export default Login;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
const rootElement = document.getElementById('root');
ReactDOM.render(
<BrowserRouter basename={baseUrl}>
<App />
</BrowserRouter>,
rootElement);
registerServiceWorker();
I just get a blank window in the browser. Nothing is rendered!
What is the problem?
You are mounting a router inside another router.
In ReactDom.render you're wrapping your app in BrowserRouter.
import { BrowserRouter } from 'react-router-dom';
// ...
ReactDOM.render(
<BrowserRouter basename={baseUrl}> // <-- this is the outer browser router
<App />
</BrowserRouter>,
rootElement
);
However inside your app you mount another BrowserRouter in your render method.
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
// ...
render () {
return (
<Router> // <-- this is the nested browser router
<Routes>
<Route path="/" element={ <Profile /> } />
<Route path="/Login" element={ <Login /> } />
</Routes>
</Router>
);
}
To fix the problem simply remove one or the other.
The rest of your code looks fine.

React router dom routing to ItemDetail

I am doing a personal React.js project. I am trying to use react-router-dom, but I haven't been able to make it work. I did the BrowserRouter in the App.js. Till there the app works fine, but I cannot make the routing redirect dynamically to a map item. I tried to follow the documentation and some tutorials unsuccesfully. The data comes from the Star Wars API This is the code:
App.js:
import './App.css';
import { Route, BrowserRouter as Router, Routes } from "react-router-dom";
import Home from './components/Home';
import MovieDetail from './components/MovieDetail'
import Navbar from './components/Navbar';
function App() {
return (
<Router>
<>
<Navbar />
<Routes>
<Route exact path='/' element={<Home />} />
</Routes>
<Routes>
<Route exact path to='/:movieId' element={<MovieDetail />} />
</Routes>
</>
</Router>
);
}
export default App;
ItemDetail:
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
const MovieDetail = () => {
const { movieId } = useParams();
const [result, setResult] = useState([]);
const fetchData = async () => {
const res = await fetch("https://www.swapi.tech/api/films/");
const json = await res.json();
setResult(json.result);
}
useEffect(() => {
fetchData();
}, []);
let movieMatch = (result.find(value) => value.properties.title == movieId)
return (
<div>
<h2>
{result
.find((value) => {value.properties.title == movieId})}
</h2>
</div>
);
}
export default MovieDetail;
UPDATE
This is a link to the whole code in codesand with updated App.js
From your code I'm assuming you're using React Router v6 in your project. Try the below code:
import './App.css';
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from './components/Home';
import MovieDetail from './components/MovieDetail'
import Navbar from './components/Navbar';
function App() {
return (
<BrowserRouter>
<Routes>
<Navbar />
<Route path='/' element={<Home />} />
<Route path=':movieId' element={<MovieDetail />} />
</Routes>
</BrowserRouter>
);
}
export default App;
Checkout React Router's Documentation for more detail.
if you are using index.js as a wrapper for app.js <BrowserRouter /> or <Router /> in your case is not used in app.js it's used in index.js otherwise it will not work
index.js should look like this : -
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
function App() {
return <h1>Hello React Router</h1>;
}
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
);
**For example just Let's say you are having "/movies" route and you want when ever your app (route = "/") starts / loads up to be redirected to "/movies" **
then wrap the routing logic with *<Switch />* ,make use of Redirect property of router dom to redirect from "/" to "/movies" and use component instead of element to render the corresponding component plus dont wrap with <Routes> </Routes> every time you are doing the route as we used it in index.js
then app.js will be : -
import './App.css';
import { Route, BrowserRouter as Router, Routes } from "react-router-dom";
import Home from './components/Home';
import MovieDetail from './components/MovieDetail'
import Navbar from './components/Navbar';
function App() {
return (
<>
<Navbar />
<Switch>
<Route exact path='/movies' component={<Home />} />
<Route exact path to='movies/:movieId' component={<MovieDetail />}
// to redirect from "/" to "/movies"
<Redirect from="/" to="/students"></Redirect>
);
}

Component cannot listen to react-router

I have a component that is used persistently across my spa. I want it to be aware of my router and the various paths that my spa is on. Is there an easy way to do this, or do I have to bandaid some redux (or something similar) state solution that is always listening to my router changes? Thanks! You can see the below for an example.
index.jsx:
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router';
import { Route, Switch } from 'react-router-dom';
import { history, store } from './redux/store';
import Navigation from './navigation';
const UserReport = () => <h2>User Report</h2>;
const UserPage = () => <h2>User Page</h2>;
const Routes = () => (
<React.Fragment>
<Route component={Navigation} />
<Switch>
<Route exact path="/users/:startDate" component={UserReport} />
<Route exact path="/users/:userId" component={UserPage} />
</Switch>
</React.Fragment>
);
render(
<Provider store={store}>
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
</Provider>, document.getElementById('app'),
);
navigation.jsx:
import React from 'react';
import { withRouter } from 'react-router-dom';
const Navigation = (props) => {
console.log(props.match.path);
// expected: "/users/:startDate"
// received: "/"
return (
<h2>Navigation</h2>
);
};
export default withRouter(Navigation);
Since the Navigation route doesn't have any path specified, it always matches whatever path you're on but the match.path only shows you the minimum path required to match for the navigation. That's why it's always /.
You can use location.pathname but it gives you the matched value and not the matched path.
const Navigation = props => {
console.log(props.location.pathname);
// prints `/users/1` if you're on https://blah.com/users/1
// prints `/users/hey` if you're on https://blah.com/users/hey
return <h2>Navigation</h2>;
};
Not sure that's what you want but if you expand what exactly you're trying to achieve, maybe I can help more.
Moreover, your second route to path="/users/:userId" overshadows the first route. Meaning there is no way to tell if hey in /users/hey is startDate or userId. You should introduce a separate route like path="/users/page/:userId".
I ended up using this react-router github discussion as my solution.
An example of my implementation:
index.jsx:
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router';
import { Route, Switch } from 'react-router-dom';
import { history, store } from './redux/store';
import Layout from './layout';
const home = () => <h2>Home Page</h2>;
const users = () => <h2>Users</h2>;
const userPage = () => <h2>User Page</h2>;
const layoutRender = component => route => <Layout component={component} route={route} />;
const Routes = () => (
<Switch>
<Route exact path="/" component={layoutRender(home)} />
<Route exact path="/users" component={layoutRender(users)} />
<Route exact path="/users/:id" component={layoutRender(userPage)} />
</Switch>
);
render(
<Provider store={store}>
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
</Provider>, document.getElementById('app'),
);
layout.jsx:
import React from 'react';
const Layout = (props) => {
const {
component: Component,
route,
} = props;
return (
<div>
<h1>This is the layout</h1>
<Component route={route} />
</div>
);
};
export default Layout;

You should not use Route outside a Router after use of withRouter

I have to use this.props.history.push('/...') in a nested component so I added withRouter() to navigate without history problems using react-router-dom.
But since I have added withRouter, I have You should not use Route outside a Router error.
I have read posts about this error but I can't understand what is wrong with my code.
Root.js:
import { BrowserRouter as Router, Route, Switch, Redirect, withRouter} from 'react-router-dom'
...
const Root = ({ store }) => (
<Router>
<Provider store={store}>
<StripeProvider apiKey="pk_test_XXXXXXXXX">
<Switch>
<Route exact path="/" component={App} />
<Route path="/comp1" component={Comp1} />
<Route path="/comp2" component={Comp2} />
<Route path="/store" component={MyStoreCheckout} />
<Route component={Notfound} />
</Switch>
</StripeProvider>
</Provider>
</Router>
)
Root.propTypes = {
store: PropTypes.object.isRequired
}
export default withRouter(Root)
and index.js:
import { createStore } from 'redux'
import myReducer from './redux/Reducers/myReducer'
import Root from './Root'
import Store from './redux/Store/store'
render(<Root store={Store} />, document.getElementById('root'))
I use withRouter to be able to call this.props.history(...) in CheckoutForm
MyStoreCheckout.js:
class MyStoreCheckout extends React.Component {
render() {
return (
<Elements>
<InjectedCheckoutForm />
</Elements>
);
}
}
export default MyStoreCheckout;
CheckoutForm.js:
class CheckoutForm extends React.Component {
handleSubmit = () => {
fetch(getRequest)
.then((response) => response.json())
.then(...)
.then(() => this.goToSuccessPage())
}
goToSuccessPage(){
this.props.history.push('/') ; //----- error is here if I have no withRouter
render() {
return (
<form onSubmit={this.handleSubmit}>
<DetailsSection/>
<CardSection />
<button>Confirm order</button>
</form>
);
}
}
export default injectStripe(CheckoutForm);
As I mentioned in my comment... Just import withRouter at the top of your CheckoutForm file, then wrap the export with it, at the bottom. Like this:
CheckoutForm.js:
import { withRouter} from 'react-router-dom'
class CheckoutForm extends React.Component {
// ... your class code ...
}
export default withRouter(injectStripe(CheckoutForm));
If your injectStripe HOC doesn't pass all of the props from withRouter down to CheckoutForm, you can try doing export default injectStripe(withRouter(CheckoutForm)); instead, but order shouldn't matter (if set up correctly)

Categories