Pass id as props in react-router-dom - javascript

I would like to pass an ID in props to a component react
I use react-router-dom
Here is my app.js file
<Switch location={this.props.location}>
<Route exact path="/" component={Home} />
<Route path="/list" component={List} />
<Route path='/img/:imgId' component={() => <Img imgId={this.props.params.imgId}/>} />
</Switch>
When I go to the next url img / 2, the router sends me the right page, but the id is not present in the props.
When I look into react developer tools on chrome, I can see that
<Switch>
<Route>
<component>
<Img>
</Img>
</component>
</Route>
</Switch>
In the component called component, I have something in props.match.params.imgId
But when I go on the Img component, here is what I have as props:
imgId: {empty object}
Do you know how to recover the id in parameter?
Thanks :)

You should do it like this:
1st: change your Route declaration
<Switch location={this.props.location}>
<Route exact path="/" component={Home} />
<Route path="/list" component={List} />
<Route path='/img/:imgId' component={Img} />
</Switch>
2nd: you should access the prop from match injected by react-router like in this example
const Img = ({ match }) => (
<div>
<h3>IMAGE ID: {match.params.imgId}</h3>
</div>
);
but of course you can easily adapt that code into your own.

You would use the functional callback pattern in case where you want to pass some props other than router props to the component. In your case you can simply render the Img component
<Switch location={this.props.location}>
<Route exact path="/" component={Home} />
<Route path="/list" component={List} />
<Route path='/img/:imgId' component={Img} />
</Switch>
and access the imgId in Img component like this.props.match.params.id.
However to point out the problem in your current code, it doesn't work correctly because you are trying to pass the parents match props to the Img component whereas you need to pass the Route's own props like
<Switch location={this.props.location}>
<Route exact path="/" component={Home} />
<Route path="/list" component={List} />
<Route path='/img/:imgId' component={(routerProps) => <Img imgId={routerProps.match.params.imgId}/>} />
</Switch>

Related

Converting from React-Router-dom v5 to v6 page contents not showing

I am having issues converting my code from meeting React-Router-Dom v5 requirements to V6 requirements. For some reason my webpage contents are not loading in the browser. Any help please? Ive tried doing the research and implementing different solutions but I havnt been able to fix the issue.
Here is my App.js
https://i.stack.imgur.com/USV67.png
Here is my index.js
https://i.stack.imgur.com/KG96P.png
Move TopBar out of the Routes component and move the Single component onto the route's element prop. The only valid children of the Routes component are the React.Fragment and Route components, and the only valid children of the Route component are other Route components.
Example:
function App() {
const user = false;
return (
<>
<TopBar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/register" element={<Register />} />
<Route path="/login" element={<Login />} />
<Route path="/write" element={<Write />} />
<Route path="/settings" element={<Settings />} />
<Route path="/post/:postId" element={<Single />} />
</Routes>
</>
);
}

Is it an incorrect practice to use URL params as a way to fetch data using react router in an react application?

In my app I have a Home page that has a child named Posts the routes are set up in the following manner:
<Route path='/' element={<Home />}>
<Route path='/posts/popular' element={<Posts />} />
<Route path='/posts/new' element={<Posts />} />
</Route>
I would like to set it up where if I am on the popular path then my api call will be:
axios.get('/posts?sort=-popular')
But if I am on new, then the call would be:
axios.get('/posts?sort=-createdAt')
The way I was thinking of implementing it was to make the second param into a selector like:
<Route path='/' element={<Home />}>
<Route path='/posts/:sortBy' element={<Posts />} />
</Route>
// in my Posts component I would call useParams
const {sortBy} = useParams();
// then in useEffect
axios.get(`/posts?sort=-${sortBy})
But this feels off, like I am doing it wrong. What is a better way, if any, of implementing this functionality?
What you did is ok, but it will make the component harder to reuse, also if you changed the path you will need to change also the component Posts. It’s better to add a new prop sortBy inside your component Posts and pass the prop inside your Route component.
<Route path='/' element={<Home />}>
<Route path='/posts/popular' element={<Posts sortBy="popular" />} />
<Route path='/posts/new' element={<Posts sortBy="new"/>} />
</Route>
You can pass props into the <Posts /> component in your Route.
Here's an example:
<Route path='/' element={<Home />}>
<Route path='/posts/popular' element={<Posts sort="popular" />} />
<Route path='/posts/new' element={<Posts sort="createdAt" />} />
</Route>
Then inside Posts you can use the prop to determine which call to make:
const Props = ({ sort }) => {
// then in useEffect
axios.get(`/posts?sort=-${sort})

Error: Functions are not valid as a React child. Unsure of where error is

This is one of the two errors that I have encountered on the same application from my previous question. Here is the first error:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
Routes#http://localhost:3000/React-Portfolio/static/js/bundle.js:40930:7
div
div
Header#http://localhost:3000/React-Portfolio/static/js/bundle.js:581:1
Router#http://localhost:3000/React-Portfolio/static/js/bundle.js:40867:7
BrowserRouter#http://localhost:3000/React-Portfolio/static/js/bundle.js:40344:7
div
App
I am not sure how to debug this entirely so not really sure where it is saying the error exists... Here is the repository: https://github.com/kstaver/React-Portfolio
With react-router v6, you must pass an element as the value of element attribue on Route component, but you are sendeing component as element. To solve the problem you need to change <Route path="/about" element={About} /> to <Route path="/about" element={<About />} /> in all your routes. Actually your routes must be configured like this:
<Routes>
<Route exact path="/" element={<Navigate to="/about" replace/>} />
<Route path="/about" element={<About />} />
<Route path="/portfolio" element={<Portfolio />} />
<Route path="/contact" element={<Contact />} />
<Route path="/resume" element={<Resume />} />
</Routes>

React router nested routes with exact path

Please help. I'm trying to make nested routes but here is a problem: if I use exact path, I can't have nested routes.
For example, I want to have one route that is nested and one individual. I have to use exact if I want to have an individual one. How can I have both?
<Route exact path="/projects" component={Projects} />
<Route path="/projects/individual" component={ProjectsList} />
<Route path="/projects/nested" component={ProjectsList} />
Here is codesandbox.
You are right, with an exact attribute you loose the flexibility to use nested routes. The solution here is to make use of Switch and order your routes such that the prefix paths are at the end
<Switch>
<Route path="/projects" component={Projects} />
<Route path="/" component={Home} />
</Switch>
and inside Projects you can write the nested paths
<Route path="/projects/individual" component={ProjectsList} />
<Route path="/projects/nested" component={ProjectsList} />
Update:
If however on ProjectsList component i.e paths "/projects/individual" and "/projects/nested" you do not want to render Projects component you would use them like
<Switch>
<Route path="/projects/individual" component={ProjectsList} />
<Route path="/projects/nested" component={ProjectsList} />
<Route path="/projects" component={Projects} />
<Route path="/" component={Home} />
</Switch>

How to set the DefaultRoute to another Route in React Router

I have the following:
<Route name="app" path="/" handler={App}>
<Route name="dashboards" path="dashboards" handler={Dashboard}>
<Route name="exploreDashboard" path="exploreDashboard" handler={ExploreDashboard} />
<Route name="searchDashboard" path="searchDashboard" handler={SearchDashboard} />
<DefaultRoute handler={DashboardExplain} />
</Route>
<DefaultRoute handler={SearchDashboard} />
</Route>
When using the DefaultRoute, SearchDashboard renders incorrectly since any *Dashboard needs to rendered within Dashboard.
I would like for my DefaultRoute within the "app" Route to point to the Route "searchDashboard". Is this something that I can do with React Router, or should I use normal Javascript (for a page redirect) for this?
Basically, if the user goes to the home page I want to send them instead to the search dashboard. So I guess I'm looking for a React Router feature equivalent to window.location.replace("mygreathostname.com/#/dashboards/searchDashboard");
You can use Redirect instead of DefaultRoute
<Redirect from="/" to="searchDashboard" />
Update 2019-08-09 to avoid problem with refresh use this instead, thanks to Ogglas
<Redirect exact from="/" to="searchDashboard" />
Source:
https://stackoverflow.com/a/43958016/3850405
Update for version 6.4.5 to 6.8.1 <:
Use replace={true} for Navigate component.
<Routes>
<Route path="/" element={<Navigate to="/searchDashboard" replace={true} />}>
<Route path="searchDashboard" element={<SearchDashboard/>} />
<Route
path="*"
element={<Navigate to="/" replace={true} />}
/>
</Route>
</Routes>
https://reactrouter.com/en/6.4.5/components/navigate
https://reactrouter.com/en/6.8.1/components/navigate
Thanks to #vicky for pointing this out in comments.
Update:
For v6 you can do it like this with Navigate. You can use a "No Match" Route to handle "no match" cases.
<Routes>
<Route path="/" element={<Navigate to="/searchDashboard" />}>
<Route path="searchDashboard" element={<SearchDashboard/>} />
<Route
path="*"
element={<Navigate to="/" />}
/>
</Route>
</Routes>
https://reactrouter.com/docs/en/v6/getting-started/tutorial#adding-a-no-match-route
https://stackoverflow.com/a/69872699/3850405
Original:
The problem with using <Redirect from="/" to="searchDashboard" /> is if you have a different URL, say /indexDashboard and the user hits refresh or gets a URL sent to them, the user will be redirected to /searchDashboard anyway.
If you wan't users to be able to refresh the site or send URLs use this:
<Route exact path="/" render={() => (
<Redirect to="/searchDashboard"/>
)}/>
Use this if searchDashboard is behind login:
<Route exact path="/" render={() => (
loggedIn ? (
<Redirect to="/searchDashboard"/>
) : (
<Redirect to="/login"/>
)
)}/>
I was incorrectly trying to create a default path with:
<IndexRoute component={DefaultComponent} />
<Route path="/default-path" component={DefaultComponent} />
But this creates two different paths that render the same component. Not only is this pointless, but it can cause glitches in your UI, i.e., when you are styling <Link/> elements based on this.history.isActive().
The right way to create a default route (that is not the index route) is to use <IndexRedirect/>:
<IndexRedirect to="/default-path" />
<Route path="/default-path" component={DefaultComponent} />
This is based on react-router 1.0.0. See https://github.com/rackt/react-router/blob/master/modules/IndexRedirect.js.
UPDATE : 2020
Instead of using Redirect, Simply add multiple route in the path
Example:
<Route exact path={["/","/defaultPath"]} component={searchDashboard} />
Jonathan's answer didn't seem to work for me. I'm using React v0.14.0 and React Router v1.0.0-rc3. This did:
<IndexRoute component={Home}/>.
So in Matthew's Case, I believe he'd want:
<IndexRoute component={SearchDashboard}/>.
Source: https://github.com/rackt/react-router/blob/master/docs/guides/advanced/ComponentLifecycle.md
Since V6 was released recently, the accepted answer won't work since Redirect no more exists in V6. Consider using Navigate.
<Route path="/" element={<Navigate to="/searchDashboard" />} />
Ref:- V6 docs
import { Route, Redirect } from "react-router-dom";
class App extends Component {
render() {
return (
<div>
<Route path='/'>
<Redirect to="/something" />
</Route>
//rest of code here
this will make it so that when you load up the server on local host it will re direct you to /something
May 2022
Import Navigate
import { Routes, Route, Navigate } from 'react-router-dom';
Add
<Route path="/" element={<Navigate replace to="/home" />} />
For example:
import React from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import Home from './pages/Home';
import Login from './pages/Login';
const Main = () => {
return (
<Routes>
<Route path="/" element={<Navigate replace to="/home" />} />
<Route path='home' element={<Home />}></Route>
<Route path='login' element={<Login />}></Route>
</Routes>
);
}
export default Main;
Done!
I ran into a similar issue; I wanted a default route handler if none of the route handler matched.
My solutions is to use a wildcard as the path value. ie
Also make sure it is the last entry in your routes definition.
<Route path="/" component={App} >
<IndexRoute component={HomePage} />
<Route path="about" component={AboutPage} />
<Route path="home" component={HomePage} />
<Route path="*" component={HomePage} />
</Route>
For those coming into 2017, this is the new solution with IndexRedirect:
<Route path="/" component={App}>
<IndexRedirect to="/welcome" />
<Route path="welcome" component={Welcome} />
<Route path="about" component={About} />
</Route>
<Route name="app" path="/" handler={App}>
<Route name="dashboards" path="dashboards" handler={Dashboard}>
<Route name="exploreDashboard" path="exploreDashboard" handler={ExploreDashboard} />
<Route name="searchDashboard" path="searchDashboard" handler={SearchDashboard} />
<DefaultRoute handler={DashboardExplain} />
</Route>
<Redirect from="/*" to="/" />
</Route>
The preferred method is to use the react router IndexRoutes component
You use it like this (taken from the react router docs linked above):
<Route path="/" component={App}>
<IndexRedirect to="/welcome" />
<Route path="welcome" component={Welcome} />
<Route path="about" component={About} />
</Route>
Firstly u need to install:
npm install react-router-dom;
Then u need to use your App.js (in your case it can be different) and do the modification below.
In this case I selected the Redirect to get proper rendering process.
import { BrowserRouter as Router, Route, Switch, Redirect } from "react-router-dom";
<Router>
<Suspense fallback={<Loading />}>
<Switch>
<Route exact path="/">
<Redirect to="/Home" component={Routes.HomePage}/>
</Route>
<Route exact path="/Biz" component={Routes.Biz} />
</Switch>
</Suspense>
</Router>
U successfully do the modification above u can see the redirect URL is on your browser path and rendering process also working properly according to their component.
Some time ago, we had an opportunity to use the component named "DefaultRoute" in the react routing.
Now, its depreciated method, and it’s not so popular to use it, you can create the custom route named default or whatever, but still, it’s not how we do it in modern React.js development.
It’s just because using the "DefaultRoute" route, we can cause some rendering problems, and its the thing that we definitely would like to avoid.
Here is how I do it-
<Router>
<div className="App">
<Navbar />
<TabBar />
<div className="content">
<Route exact path={["/default", "/"]}> //Imp
<DefStuff />
</Route>
<Route exact path="/otherpage">
<Otherstuff />
</Route>
<Redirect to="/defult" /> //Imp
</div>
</div>
</Router>
Use:
<Route path="/" element={<Navigate replace to="/expenses" />} />
In context:
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
<BrowserRouter>
<Routes>
<Route element={<App />}>
<Route path="/expenses" element={<Expenses />} />
<Route path="/invoices" element={<Invoices />} />
</Route>
<Route path="/" element={<Navigate replace to="/expenses" />} />
</Routes>
</BrowserRouter>
You use it like this to redirect on a particular URL and render component after redirecting from old-router to new-router.
<Route path="/old-router">
<Redirect exact to="/new-router"/>
<Route path="/new-router" component={NewRouterType}/>
</Route>

Categories