react-router-dom match object isExact false - reactjs

I am working on a react project. I try to access the url parameters in the Header component. However, it always returns empty.
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { ConnectedRouter } from 'connected-react-router'
import SamplePage from './pages/SamplePage';
import PropertyPage from './pages/PropertyPage';
import LoadingPage from './pages/LoadingPage';
import Header from './header/Header';
import ButtonGroup from './ButtonGroup';
import { Container } from 'semantic-ui-react';
import history from '../history';
const App = () => {
return (
<ConnectedRouter history={history}>
<div>
<Switch>
<Route path='/loading' exact component={LoadingPage} />
<Route component={Header} title='Sample page' />
</Switch>
<Container style={{ marginTop: '7em' }}>
<Switch>
<Route
path='/page/:pageType/properties/:propertyId'
exact
component={PropertyPage}
/>
<Route path='/page/:pageType' exact component={SamplePage} />
</Switch>
</Container>
<Switch>
<Route exact path='/loading' render={() => <div />} />
<Route component={ButtonGroup} />
</Switch>
</div>
</ConnectedRouter>
);
}
export default App;
I try to access url params in the Header component. The params is empty, and isExact is false. Can anyone help me with this? Thanks.

From screenshot of console.log, react-router is matching on
<Route component={Header} title='Sample Scorecard' />
This is correct behavior as Switch looks for the first match.
I suggest to not declare rendering for Header as a Route. i.e.
<Switch>
<Route path='/loading' exact component={LoadingPage} />
<Header title='Sample Scorecard' />
</Switch>
This way Switch will only render it when loading path isn't matched.

I still cannot figure out how to solve this issue. What I do to walk around this issue is to create a Higher Order Component. Header will be included in the HOC, then it has no problem to get the URL parameters.

Related

How to include Routes in React without making the webpage blank?

I am trying to make a website by watching tutorial in Udemy.
The instructor added Route in one of the file, and I did the same, but my webpage becomes blank after adding it.
I also tried including Routes but that didn't help.
Here's my code:
import { Container } from 'react-bootstrap';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Header from './components/Header';
import Footer from './components/Footer';
import HomeScreen from './screens/HomeScreen';
function App() {
return (
<Router>
<Header />
<main classname="py-3">
<Container>
<Routes>
<Route path="/" element={<HomeScreen />} />
</Routes>
</Container>
</main>
</Footer />
</Router>
);
}
export default App;
enter image description here
It looks like you may be using react-router-dom#6. The Routes component API changed significantly from v5 to v6, it no longer takes a component, or render or children function props, all replaced by a single element prop taking a ReactNode, a.k.a. JSX. Note that in RRDv6 that all routes are now always exactly matched, so there is also no longer any exact prop.
Example:
<Router>
<Header />
<main classname="py-3">
<Container>
<Routes>
<Route path="/" element={<HomeScreen />} />
</Routes>
</Container>
</main>
</Footer />
</Router>

React -router doesn't add component to the dom

I have Quotes component which contains list of quotes. When I click individual list element, react router changes the url dynamically and opens it in full page which is Full_Screen_Quotes then again inside the Full_Screen_Quotes I want to add Comments section on button click with the help of react router, but it does not work for the reason I do not know. What might be the reason?
import { useParams } from "react-router-dom/cjs/react-router-dom.min"
import Comments from "./Comments"
import classes from '../css/Full_Screen_Quote.module.css'
import db from "./db"
import { useState } from "react"
import { Router,Route } from "react-router-dom/cjs/react-router-dom.min"
import { Link } from "react-router-dom"
import { Switch } from "react-router-dom/cjs/react-router-dom.min"
const Full_Screen_Quotes = ()=>{
const params = useParams()
return(
<>
<div className={classes.quote}>
<h1>{params.quoteId} </h1>
<h4>{params.authorId}</h4>
<Link to = {`/Allquotes/${params.authorId}/${params.quoteId}/comments`}> // Link that adds comment section
<button>ADD</button>
</Link>
</div>
<Route path ={`/Allquotes/${params.authorId}/${params.quoteId}/comments` } exact > //Comment section path
<Comments></Comments>
</Route>
</>
)
}
export default Full_Screen_Quotes
App js
import logo from './logo.svg';
import './App.css';
import {Route} from 'react-router-dom'
import {Switch} from 'react-router-dom'
import {Link} from'react-router-dom'
import MainHeader from '../src/components/MainHeader'
import Quotes from '../src/components/Quotes'
import AddQuotes from './components/AddQuotes';
import Welcome from './components/Welcome'
import {useParams} from 'react-router-dom'
import ListElement from './components/ListElement';
import Full_Screen_Quote from '../src/components/Full_Screen_Quote'
import {Redirect} from 'react-router-dom'
import Home from '../src/components/Home'
import NotFound from './NotFound';
import {useState,useCallback,useMemo} from 'react'
import { Prompt } from 'react-router-dom/cjs/react-router-dom.min'
import Comments from './components/Comments';
function App() {
const [is_focused,set_is_focused] = useState(false)
return (
<div>
<Switch>
<Route path ='/Welcome/' exact>
<Welcome/>
</Route>
<Route path ='/Allquotes/' exact>
<Quotes />
</Route>
<Route path ='/Addquote/' exact>
<AddQuotes set_is_focused = {set_is_focused} is_focused={is_focused} />
</Route>
<Route path ='/Allquotes/:authorId/:quoteId' exact>
<Full_Screen_Quote />
</Route>
</Switch>
</div>
)
}
export default App;
Comments js
import { useState } from "react"
import db from '../components/db'
import { useRef } from "react"
const Comments = (props)=>{
return (
<div style={{display:'flex',justifyContent:'center'}}>
<div> <textarea style={{transition:'1s all'}}></textarea> </div>
</div>
)}
export default Comments
Your root router/Switch is exactly matching URL paths, and in the case of rendering the Full_Screen_Quote component it only exactly matches up to '/Allquotes/:authorId/:quoteId'
<Route path ='/Allquotes/:authorId/:quoteId' exact>
<Full_Screen_Quote />
</Route>
As soon as the path becomes "/Allquotes/someAuthorId/someQuoteId}/comments" it no longer matches exactly and your Full_Screen_Quote component is unmounted, thus unmounting the nested Route you want to render for the Comments component.
In 99.99% for use cases there's really no need to use the exact prop in conjunction with the Switch since you can order the rendered routes in inverse order of path specificity so matching can work properly. Using the exact prop also necessarily precludes the further matching of any nested routes.
You've a couple options:
Order the routes in the Switch in descending path specificity order and remove the exact prop. This allows the nested route in Full_Screen_Quote to be eventually matched and rendered.
<Switch>
<Route path='/Welcome/'>
<Welcome />
</Route>
<Route path='/Allquotes/:authorId/:quoteId'>
<Full_Screen_Quote />
</Route>
<Route path='/Allquotes/'>
<Quotes />
</Route>
<Route path='/Addquote/'>
<AddQuotes
set_is_focused={set_is_focused}
is_focused={is_focused}
/>
</Route>
</Switch>
...
const Full_Screen_Quotes = () => {
const { path, url } = useRouteMatch();
const params = useParams();
return (
<>
<div className={classes.quote}>
<h1>{params.quoteId} </h1>
<h4>{params.authorId}</h4>
<Link to={`/${url}/comments`}>
<button>ADD</button>
</Link>
</div>
<Route path={`${path}/comments`}>
<Comments />
</Route>
</>
);
};
Move the nested route out to the main router/Switch and order the routes in descending path specificity order and remove the exact prop.
<Switch>
<Route path='/Welcome/'>
<Welcome />
</Route>
<Route path='/Allquotes/:authorId/:quoteId/comments'>
<Comments />
</Route>
<Route path='/Allquotes/:authorId/:quoteId'>
<Full_Screen_Quote />
</Route>
<Route path='/Allquotes/'>
<Quotes />
</Route>
<Route path='/Addquote/'>
<AddQuotes
set_is_focused={set_is_focused}
is_focused={is_focused}
/>
</Route>
</Switch>
...
const Full_Screen_Quotes = () => {
const { url } = useRouteMatch();
const params = useParams();
return (
<div className={classes.quote}>
<h1>{params.quoteId} </h1>
<h4>{params.authorId}</h4>
<Link to={`/${url}/comments`}>
<button>ADD</button>
</Link>
</div>
);
};
wrap the app.js in a router
import {BrowserRouter as Router} from 'react-router-dom'
then
<Router>
<Switch>
<Route path ='/Welcome/' exact>
<Welcome/>
</Route>
<Route path ='/Allquotes/' exact>
<Quotes />
</Route>
<Route path ='/Addquote/' exact>
<AddQuotes set_is_focused = {set_is_focused} is_focused={is_focused} />
</Route>
<Route path ='/Allquotes/:authorId/:quoteId' exact>
<Full_Screen_Quote />
</Route>
</Switch>
</Router>

ReactJS - I am getting the error"r Invariant failed - not use <Link> outside a <Router>" - this happens in one page but not other pages

In one of my modules I use this code without any issue:
import { Link } from "react-router-dom";
<Link to="/HowItWorks">
Continue
</Link>
But in another module I use similar code but get the invarient failed message
import { Link } from "react-router-dom";
<Link to="/TheBook">Continue</Link>
The only difference is that the first module is in the src/components directory while the failing module is in the src directory.
In App.js (which is also in src directory) the router code includes several modules including both of the ones above:
import {
Switch,
BrowserRouter as Router,
Route,
Redirect,
} from 'react-router-dom';
import Nav from './Nav';
import Introduction from './components/Introduction.js';
import HowItWorks from './components/HowItWorks.js';
import Blog from './components/Blog.js';
import Shop from './components/Shop.js';
import TheBook from './components/TheBook.js';
import Footer from './Footer.js';
function App() {
return (
<>
<div className="App">
<Router>
<Nav />
<Switch>
<Route path="/Introduction" exact component={Introduction}></Route>
<Route path='/HowItWorks' exact component={HowItWorks}></Route>
<Route path="/Shop" exact component={Shop}></Route>
<Route path="/Blog" exact component={Blog}></Route>
<Route path="/TheBook" exact component={TheBook}></Route>
</Switch>
</Router>
<Footer />
</div>
</>
);
}
export default App;
If I click the link for "TheBook" in the navbar it works fine... Any idea why is this happening ?
Ok, the problem was, as I expected, not in the footer.js code but in the App.js code. The footer.js code needed to be moved inside the Router:
function App() {
return (
<>
<div className="App">
<Router>
<Footer /> {/* The footer must be inside the router for the <link> to work */}
<Nav />
<Switch>
<Route path="/Introduction" exact component={Introduction}></Route>
<Route path='/HowItWorks' exact component={HowItWorks}></Route>
<Route path="/Shop" exact component={Shop}></Route>
<Route path="/Blog" exact component={Blog}></Route>
<Route path="/TheBook" exact component={TheBook}></Route>
</Switch>
</Router>
</div>
</>
);
}
This is now working properly.

How to make the drawer appear only on private routes?

I am developing a system with a login, and I want the drawer to appear only on routes that are private. How can I do that, below is a code that I already tried and was not successful.
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Home from "./Home";
import Login from "./Login";
import Details from "./Details";
import { AuthProvider } from "./Auth";
import PrivateRoute from "./PrivateRoute";
import Drawer from "./components/Drawer";
const App = () => {
return (
<AuthProvider>
<Router>
<Route exact path="/login" component={Login} />
</Router>
<Router>
<RoutePrivate />
</Router>
</AuthProvider>
);
};
const RoutePrivate = () => {
return (
<Router>
<Drawer />
<PrivateRoute exact path="/" component={Home} />
<PrivateRoute exact path="/:id" component={Details} />
</Router>
);
};
export default App;
You could do conditional rendering of drawer
const RoutePrivate = () => {
return (
<Router>
{window.location.pathname !== '/login' ? <Drawer /> : null}
<PrivateRoute exact path="/" component={Home} />
<PrivateRoute exact path="/:id" component={Details} />
</Router>
);
};
Good luck
You can go with few options.
you can remove <Drawer /> from router and render it inside <PrivateRoute /> along with matched router component (Home or Details etc).
Inside Drawer component, you can check auth and render null if auth failed. This way public routes will never get to see Drawer compoennt.
create a common layout and put the router inside layout. Check this. Also I got a random codesandbox (not fully working) but you can see the code reference there.

passing props to component during routing

import React from 'react';
import SearchDocument from './components/searchDocument';
import CreateRequest from './components/createRequest';
import { BrowserRouter as Router } from "react-router-dom";
import { Route, Switch } from 'react-router-dom';
class App extends React.Component {
render() {
return (
<Router>
<div>
<Switch>
<Route exact path='/' component={Home} />
<Route path='/home' component={Home} />
<Route path='/create' component={CreateRequest} />
<Route path='/searchDocument' component{SearchDocument} />
<Route path='/upload' component={UploadDocument} />
<Route path='/search' component={Search} />
</Switch>
</div>
</Router>
);
}
}
export default App;
I am new to react router.I want to pass props through route to create request component.I tried different methods to pass props but that doesn't work.Can anyone please suggest, how to send props to component and how to handle them in that component.Above is my code.Thanks in advance
As mentioned in the comments, you can use the render prop instead of the component.
From the docs:
Instead of having a new React element created for you using the component prop, you can pass in a function to be called when the location matches. The render prop receives all the same route props as the component render prop.
And an example with your code will look like this:
<Route path='/searchDocument' render={(props) => <SearchDocument yourNewProp={yourNewProp} {...props} />} />

Resources