React Router 4 Nested Routes not in Props [duplicate] - javascript

I've upgraded the react router to version 4 in my application. But now I'm getting the error
Warning: You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored
What is wrong with this routing?
import {
Switch,
BrowserRouter as Router,
Route, IndexRoute, Redirect,
browserHistory
} from 'react-router-dom'
render((
<Router history={ browserHistory }>
<Switch>
<Route path='/' component={ Main }>
<IndexRoute component={ Search } />
<Route path='cars/:id' component={ Cars } />
<Route path='vegetables/:id' component={ Vegetables } />
</Route>
<Redirect from='*' to='/' />
</Switch>
</Router>
), document.getElementById('main'))

IndexRoute and browserHistory are not available in the latest version, also Routes do not accept children Routes with v4, Instead, you can specify Routes within the component Itself
import {
Switch,
BrowserRouter as Router,
Route, Redirect
} from 'react-router-dom'
render((
<Router>
<Switch>
<Route exact path='/' component={ Main }/>
<Redirect from='*' to='/' />
</Switch>
</Router>
), document.getElementById('main'))
Then in the Main Component
render() {
const {match} = this.props;
return (
<div>
{/* other things*/}
<Route exact path="/" component={ Search } />
<Route path={`${match.path}cars/:id`} component={ Cars } />
</div>
)
}
Similarly in the cars component
you will have
render() {
const {match} = this.props;
return (
<div>
{/* other things*/}
<Route path={`${match.path}/vegetables/:id`} component={ Vegetables } />
</div>
)
}

Nested routes are not available from version react-router 4.x. Here is a basic example straight from react-router documentation on how to code for nesting route secnarios in v4.x.
Also have a look on this question as well Why can I not nest Route components in react-router 4.x?

Related

React router v6: catch all "*" path does not work when using nested routes

I have the following two files
AppRoutes.tsx
import { Route, Routes } from "react-router-dom";
import NotFound from "../pages/NotFound";
import MessageRoutes from "../features/messages/routes/MessageRoutes";
import Home from "../pages/Home";
export default function AppRoutes() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/messages/*" element={<MessageRoutes />} />
<Route path="*" element={<NotFound />} />
</Routes>
);
}
MessageRoutes.tsx
import { Route, Routes } from "react-router-dom";
import ProtectedRoutes from "../../../routes/ProtectedRoutes";
import MessageOverview from "../pages/MessageOverview";
import NewMessage from "../pages/NewMessage";
export default function MessageRoutes() {
return (
<Routes>
<Route element={<ProtectedRoutes />}>
<Route path="/" element={<MessageOverview />} />
<Route path="/new" element={<NewMessage />} />
</Route>
</Routes>
);
}
Because I'm using the path "/messages/*" to capture all paths that start with /messages, my MessageRoutes component takes care of these nested routes. I have a final "*" route in the AppRoutes component to capture any url that the app does not support. But if the path would be "/messages/loremipsum", react router does not catch the NotFound route because everything that starts with "/messages" will be handled with the MessageRoutes component.
Does this mean that in every nested route component I now have to add a final <Route path="\*" element={\<NotFound /\>} /\> again, just to support a final catch all route? I don't like this approach. Is there no absolute final catch all for every route?
Does this mean that in every nested route component I now have to add
a final <Route path="\*" element={<NotFound />} \> again
Yes, absolutely. Each Routes component manages its own "scope" of routes for what it can match. For example if the current URL path is "/messages/loremipsum" the root Routes component matches the "/messages/*" and correctly renders the MessageRoutes component. The MessageRoutes component's Routes component then works on matching on the next path segment. Since there is no "*/loremipsum" route path you need another "catch-all" route to handle this.
The issue is that a Routes component isn't aware of what descendent routes any of its routes may possibly be rendering.
Example:
import { Route, Routes } from "react-router-dom";
import NotFound from "../pages/NotFound";
import MessageRoutes from "../features/messages/routes/MessageRoutes";
import Home from "../pages/Home";
export default function AppRoutes() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/messages/*" element={<MessageRoutes />} />
<Route path="*" element={<NotFound />} />
</Routes>
);
}
import { Route, Routes } from "react-router-dom";
import ProtectedRoutes from "../../../routes/ProtectedRoutes";
import MessageOverview from "../pages/MessageOverview";
import NewMessage from "../pages/NewMessage";
import NotFound from "../pages/NotFound";
export default function MessageRoutes() {
return (
<Routes>
<Route element={<ProtectedRoutes />}>
<Route path="/" element={<MessageOverview />} />
<Route path="/new" element={<NewMessage />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
);
}
If you want to have a single "catch-all" route then you'll need to have a single routes configuration.
Example:
import { Route, Routes } from "react-router-dom";
import MessageOverview from "../pages/MessageOverview";
import NewMessage from "../pages/NewMessage";
import NotFound from "../pages/NotFound";
import Home from "../pages/Home";
export default function AppRoutes() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route element={<ProtectedRoutes />}>
<Route path="/messages">
<Route index element={<MessageOverview />} />
<Route path="new" element={<NewMessage />} />
</Route>
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
);
}
Now when/if the URL path is "/messages/loremipsum" then this Routes component knows what nested routes it is rendering and can match and can correctly render NotFound.

Using condition for redirecting in react router dom v6

I'm currently getting troubled with the react router dom#6. Like, I want to redirect users to other pages If they logged in. But when I use Navigate component It threw error saying that :
"[Navigate] is not a component. All component children of Routes must be a Route or React.Fragment"
Though I have wrapped it in the fragment but the result still the same. Please help me, this is my code:
import {BrowserRouter as Router, Routes, Route, Navigate} from 'react-router-dom';
import Home from './pages/Home';
import ProductList from './pages/ProductList';
import Product from './pages/Product';
import Register from './pages/Register';
import Login from './pages/Login';
import Cart from './pages/Cart';
import ErrorPage from './pages/ErrorPage';
function App() {
const user = true;
return (
<Router>
<Routes>
<Route path="/" element={<Home/>} />
<Route path="/products/:category" element={<ProductList/>} />
<Route path="/product/:id" element={<Product/>} />
<Route path="/cart" element={<Cart/>} />
<Route path="/dang-ky" element={<Register/>} />
<Route path="/dang-nhap" element={<Login/>} >
{user ? <><Navigate to="/" replace/></> : <Login/>}
</Route>
<Route path="*" element={<ErrorPage/>} />
</Routes>
</Router>
);
}
export default App;
You can simply use Navigate component from 'react-router-dom' library and use it to redirect inside the element attribute of the '/login' route.
import { BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom";
<Route path="login" element={user ? <Navigate to="/"/> : <Login />} /> {/* If user is logged in, then redirect to home page, else go to login page */}
Redirecting users if logged in, necessarily does not need a navigation, you could simply just specify the element you want to reach eg:
<Route exact path="/" element={user ? <Home/> : <Login />}/>
Edit:
This is also a possible solution, drawback is that you cant redirect properly. You just have to make sure that you have the fallbackroutes set up right.
{isSignedIn && <Route path="" element={} />}
I would recommend using an AuthRout to wrap your Routes.
function AuthRoute ({children}) {
if(!user.isSignedIn){
//Not signed in
return <Navigate to="/signIn" />
}
//Signed in
return children
}
//Route itself
<Route path="/abc" element={<AuthRoute><YourComponent /></AuthRoute>} />
That way the user gets redirected if he is not signed in
sorry for the that problem, but you can use useNavigate from react-router-dom v6
as below
import { useNavigate } from 'react-router-dom';
export const MyComponent = ({user}) => {
const navigate = useNavigate();
useEffect(() => {
if(user){
navigate('/dashboard')
}else{
navigate('/login')
}
......
},[])
.....
}
and use that codes on your route logic, it makes your route more clean
<Router>
<Routes>
<Route path="/" element={<Home/>} />
<Route path="/products/:category" element={<ProductList/>} />
<Route path="/product/:id" element={<Product/>} />
<Route path="/cart" element={<Cart/>} />
<Route path="/dang-ky" element={<Register/>} />
<Route path="/dang-nhap" element={<MyComponent user = {user} />}/>
<Route path="*" element={<ErrorPage/>} />
</Routes>
</Router>

Re-use navbar component with multiple components

I have a simple question. What is the best way to use a navbar with multiple components using react router? Just let me show the code so you can understand what I'm trying to say.
import React from "react";
import "./App.css";
import {
BrowserRouter as Router,
Switch,
Route,
Redirect
} from "react-router-dom";
import Auth from "./website/Auth/Auth";
import SocialMedia from "./website/SocialMedia/SocialMedia";
import SingleUser from "./website/SingleUser/SingleUser";
import Search from "./website/Search/Search";
import SinglePhoto from "./website/SinglePhoto/SinglePhoto";
import Navbar from "./components/Navbar/Navbar";
function App() {
const logIn = JSON.parse(localStorage.getItem("token"));
return (
<Router>
<Switch>
<Route exact path="/" component={Auth}>
{logIn ? <Redirect to={`/profile/${logIn.data.id}`} /> : <Auth />}
</Route>
<Navbar>
<Route exact path="/profile/:id" component={SingleUser} />
<Route exact path="/socialmedia" component={SocialMedia} />
<Route exact path="socialmedia/search" component={Search} />
<Route exact path="socialmedia/photo/:id" component={SinglePhoto} />
</Navbar>
</Switch>
</Router>
);
}
export default App;
So I have to reuse my Navbar component, and I tried to use <Navbar />, then the other routes below, but that wasn't working, and when I put <Navbar> </Navbar> that worked and the other components will appear, but is that the way I reuse my Navbar component?
Thanks for your time !!
import React from "react";
import "./App.css";
import {
BrowserRouter as Router,
Switch,
Route,
Redirect
} from "react-router-dom";
import Auth from "./website/Auth/Auth";
import SocialMedia from "./website/SocialMedia/SocialMedia";
import SingleUser from "./website/SingleUser/SingleUser";
import Search from "./website/Search/Search";
import SinglePhoto from "./website/SinglePhoto/SinglePhoto";
import Navbar from "./components/Navbar/Navbar";
function App() {
const logIn = JSON.parse(localStorage.getItem("token"));
return (
<Router>
<Switch>
<Route exact path="/" component={Auth}>
{logIn ? <Redirect to={`/profile/${logIn.data.id}`} /> : <Auth />}
</Route>
<Route Component={Navbar}>
<Route exact path="/profile/:id" component={SingleUser} />
<Route exact path="/socialmedia" component={SocialMedia} />
<Route exact path="socialmedia/search" component={Search} />
<Route exact path="socialmedia/photo/:id" component={SinglePhoto} />
</Route>
</Switch>
</Router>
);
}
export default App;
Try this!
If you want the Navbar to render only on certain routes then render it only on certain routes. Render the Navbar into a route outside the Switch and specify all the paths you want it to be rendered on in an array on the path prop.
Additional notes:
Within the Switch component, order and path specificity matter, reorder your routes to specify more specific paths before less specific paths. This allows you to not need to specify the exact prop for every route.
Don't specify both a component prop and render children on a single Route, see Route render methods. Just render the Redirect or Auth component as children.
Code:
function App() {
const logIn = JSON.parse(localStorage.getItem("token"));
return (
<Router>
<Route
path={["/profile", "/socialmedia"]}
component={Navbar}
/>
<Switch>
<Route path="/profile/:id" component={SingleUser} />
<Route path="socialmedia/photo/:id" component={SinglePhoto} />
<Route path="socialmedia/search" component={Search} />
<Route path="/socialmedia" component={SocialMedia} />
<Route path="/">
{logIn ? <Redirect to={`/profile/${logIn.data.id}`} /> : <Auth />}
</Route>
</Switch>
</Router>
);
}

React Router Not Working Like I Need It Too

Im trying to figure out how to get my navigation bar setup as most of the UI is coming together. I have setup my index.js and also a Route.js and then linked them with my different components like so:
Index.js
import React from "react";
import ReactDOM from "react-dom";
import { Auth0Provider } from "./react-auth0-spa.js";
import { useAuth0 } from "./react-auth0-spa";
import Routes from "./Routes"
import config from "./utils/auth_config.json";
import { BrowserRouter } from "react-router-dom";
// A function that routes the user to the right place
// after login
const onRedirectCallback = appState => {
history.push(
appState && appState.targetUrl
? appState.targetUrl
: window.location.pathname
);
};
ReactDOM.render(
<Auth0Provider
domain={config.domain}
client_id={config.clientId}
redirect_uri={window.location.origin}
onRedirectCallback={onRedirectCallback}
>
<BrowserRouter>
<Routes />
</BrowserRouter>
</Auth0Provider>,
document.getElementById("root")
);
Routes.js
import React, { Component } from "react";
import { Router, Route, Switch, BrowserRouter } from "react-router-dom";
import {Link } from "react-router-dom";
import Profile from "./components/user/Profile";
import PrivateRoute from "./components/user/PrivateRoute";
import history from "./utils/history.js";
import HomePage from "./modules/HomePage.js";
import ProductPage from "./modules/ProductPage";
class Routes extends Component {
render() {
return (
<Router history={history}>
<Switch>
<Route path="/" component={HomePage} />
<Route path="/ProductPage" component={ProductPage} />
<PrivateRoute path="/profile" component={Profile} />
</Switch>
</Router>
)
}
}
export default Routes;
but when i reload my site it just continues to say localhost:8080/ProductPage like its suppose to be the default, then when i manually enter localhost:8080/ and click on a button after linking it with
<Link to="ProductPage">
it will show on the tab localhost:8080/ProductPage but wont actually redirect me to the other component, i am just wondering what i am doing wrong?
Issue
You have your "home" route listed first in the Switch.
Switch
Renders the first child <Route> or <Redirect> that matches the location.
"/" is less specific and matches basically all routes, so even though the URL is "/ProductPage", "/" still matches it and HomePage is rendered.
Solution
Either move it after the other more specific routes or use the exact prop on it.
<Router history={history}>
<Switch>
<Route path="/ProductPage" component={ProductPage} />
<PrivateRoute path="/profile" component={Profile} />
<Route path="/" component={HomePage} />
</Switch>
</Router>
or
<Router history={history}>
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/ProductPage" component={ProductPage} />
<PrivateRoute path="/profile" component={Profile} />
</Switch>
</Router>

React show routes if set up using react-router

Is there a command on the console I can execute at run-time that will tell me all the routes? I have used react-router, but the routes don't all work. In rails you can get a list at runtime.
you can get the routes in an array by using below library
https://github.com/alansouzati/react-router-to-array
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import reactRouterToArray from 'react-router-to-array';
// or var reactRouterToArray = require('react-router-to-array');
console.log(reactRouterToArray(
<Route path="/" component={FakeComponent}>
{/* just to test comments */}
<IndexRoute component={FakeComponent} />
<Route path="about" component={FakeComponent}>
<Route path="home" component={FakeComponent} />
<Route path="/home/:userId" component={FakeComponent} />
</Route>
<Route path="users" component={FakeComponent} />
<Route path="*" component={FakeComponent} />
</Route>)
); //outputs: ['/', '/about', '/about/home', '/users']

Categories