react-router routes <Link> v5 - reactjs

I am trying to understand nested react routes. My current issue is that my link outputs undefined and also renders the component on the same view instead of loading just that component view.
Events.js
const data = {
"events":[
{
"id":"1234",
"name":"Meetup",
"description":"Tech meetup"
},
{
"id":"34234",
"name":"Test",
"description":"Events"
}
]
}
{data.events.map(event => (
<Link to={`/events/${data.events.id}`}>
<EventCard data={event} />
</Link>
))}
<Switch>
<Route path={`/events/:eventId`} render={(props) => <Event {...props} />}/>
</Switch>
How can I render the Event component when a user has clicked one of the event card. I can currently load both components on the same page but that is not my desired output. The expected path should be:
Expected:
Render only
Current:
Renders & on same page

At first you have got a problem here,
<Link to={`/events/${data.events.id}`}>
Since you are already mapping event object from data array, iterator event already holds event data. So you should change it like this;
<Link to={`/events/${event.id}`}>
Also I'd strongly recommend to use 'exact' prop in your routes for routing multiple endpoints from same root like this.
<Route exact path={`/events/:eventId`} render={(props) => <Event {...props} />}/>
See here for further information. Difference between exact path and path

You need to add id param to your event endpoint:
<Route path="/events/:id?" component={Event} />
Then in the Event you can check if id is present via props.match.params.id.

Related

React router doesn't refresh page

I have two different routes that are using the same component.
<BrowserRouter>
<Routes>
<Route path="/produktai" element={<Products />}/>
<Route path="/produktai/:productId" element={<Products />} />
</Routes>
</BrowserRouter>
I'm using Link from react to switch between pages and get id's from
const pathname = useLocation().pathname;
to perform rest api's.
For example, when i change pages using Link from /products to /products/2, url changes but the problem is that page doesn't refresh as it should. Problem solves if i'm using a tag with href as it always reloads page unlike smooth react Link functionality but i don't think that's a good solution.
Even though the route path changes you are still rendering the same Products component. If you need to run some logic when the path changes, or more likely, when the productId route param updates, then you should use an useEffect hook with the productId route param as a dependency.
Example:
const { productId } = useParams();
useEffect(() => {
if (productId) {
// Perform rest api call and response handling
}
}, [productId]);
There is a better approach for this issue. Give unique keys like below.
<Route path="/all-reports">
<ReportsPage key="1" />
</Route>
<Route path="/shared-reports">
<ReportsPage key="2" mode="shared" />
</Route>
<Route path="/owned-reports">
<ReportsPage key="3" mode="owned" />
</Route>

React router: Redirecting with url param?

In my render function I have
<Route path="/classes/:course" render={(match) => (
<Redirect to={`/classes/${match.params.course}/home`} />
)}>
<Route path="/home" component={Home} />
</Route>
For example if the param for "course" was "BIO1001", I want to redirect the page to "/classes/BIO1001/home" when I go to the page "/classes/BIO1001/". Previously I tried simply putting a Redirect tag with "from" and "to" but ran into the problem of the url actually going to "/classes/:course/home" instead of "/classes/BIO1001/home"
Also would the nested route with path="/home" go to "/classes/BIO1001/home"? I was unsure on how I can set a route where the path starts from the previous url (in this case starting from "/classes/:course/"
The first problem is right here:
render={(match) => ( ...
The render function gets a props object which contains a match property. Instead of destructuring the match property, what you are actually doing is assigning the whole props object to a variable match. So when you go to access match.params it won't be found.
You need curly braces around match in order to destructure it.
render={({match}) => ( ...
The second problem is the nesting of the two Route components. I get a warning:
Warning: You should not use <Route render> and <Route children> in the same route; <Route render> will be ignored
So based on that warning you can see that your Redirect is being entirely ignored since it comes from render. The child Route is seen as the render function for the classes Route.
I'm assuming you have various subpages of a course? And we want to force the URL to include "/home" if none is set? (Personally I would do the opposite and redirect "/home" to the course root URL).
Previously I tried simply putting a Redirect tag with "from" and "to" but ran into the problem of the url actually going to "/classes/:course/home" instead of "/classes/BIO1001/home"
Per the docs, you can use params in your Redirect, but only if it is inside a Switch.
Here's a sample code to do that:
const CoursePage = () => {
// you can access arguments from the props or through hooks
const { course, tab } = useParams();
// not sure how you want to handle the different tabs
return <div>Viewing Course {course}</div>;
};
const App = () => (
<BrowserRouter>
<Switch>
<Route path="/classes/:course/:tab"><CoursePage/></Route>
<Redirect from="/classes/:course" to="/classes/:course/home"/>
</Switch>
</BrowserRouter>
);
export default App;
Your nested routing is true i think. But you are rendering your Home component without any dynamic props. Try it like below:
<Route path="/classes/:course" render={(match) => (
<Redirect to={`/classes/${match.params.course}/home`} />
)}>
<Route path="/home" >
<Home someProps={someValue} />
</Route>
</Route>
Note: The Redirect element can be used without a containing Route element by providing a from property. In this case, you can just use the URL parameter tokens in both from and to, and they'll be carried over for you. For example, if you're using a Switch block...
<Switch>
{/* various app routes... */}
{/* redirect action */}
<Redirect from="/classes/:course" to="/classes/:course/home" />
<Switch>

How to create a dynamic subheader based on route change or params in React Router v6?

In React Router v6, I'm trying to create a dynamic SubHeader component that changes based on a particular route or route param. For context, here's a contrived setup:
export const App = () => {
return (
<BrowserRouter>
<Header>
<TopHeader />
<DynamicSubHeader /> // <-- here
</Header>
<Routes>
<Route path="products" element={<Products />} />
<Route path='product/:id/*' element={<ProductDetail />} >
<Route path="price" element={<ProductDetailPrice />} />
</Route>
</Routes>
</BrowserRouter>
)
}
I'd like to update <DynamicSubHeader /> with different content when navigating between /products, /products/:id and even /products/:id/price
Inside <DynamicSubHeader />, I've tried to use the useParam hook provided by react-router-dom, but it just returns null or the data doesn't persist. It seems you can only use useParam when it's nested inside the <Routes> component.
I've also tried using the useLocation hook to listen for route change and manually setting state there, but it doesn't persist either.
For the time being, I've treated <Header /> as a Layout-component that I've placed in each route. Am I using React Router wrong? Otherwise, any suggestions on how to approach this?
To access your URL data try to use the match object with this.props.match inside your <DynamicSubHeader /> component.
This is how it looks on a sample edit route:
//$> console.log(this.props.match)
{
isExact: true
params:
id: "5tkd6w"
__proto__: Object
path: "/streams/edit/:id"
url: "/streams/edit/5tkd6w"
__proto__: Object
}
Once you've got the id you can perform almost any operation and by getting the full URL you can determine other parts of your subheader content.

React Router v4: <Route> component unable to match urls with question marks

I'm using a React.js app as a widget inside a Wordpress admin area. The way some users configure wordpress urls can use query parameters to set the page view, for example:
domain.com/wpFolder/wp-admin/options-general.php?page=settingspage
I want to be able to set my Links and Routes to conform to this structure so that page reloads don't break. I am unable to do this, adding slashes/subfolders is fine but the moment I add ? the <Route /> no longer matches. Example:
<StyledMaterialLink // This is a <Link /> wrapped by Styled Components
component={RouterLink}
to={`/wptest2/wp-admin/options-general.php?`}>
<Tab
label="See Current Coupons"
value={seeCurrentCoupons}
onClick={() => {
console.log(location.href, `=====location.href=====`);
setAdminView(seeCurrentCoupons)
}}
/>
</StyledMaterialLink>
<Route
path={`/wptest2/wp-admin/options-general.php?`}
render={props => {
return (
<CurrentCouponChannel.Provider
value={state.currentCouponsAndTargets}>
<ViewCoupons />
</CurrentCouponChannel.Provider>
)
}}
/>
Eventually I would like the <Route path /> above to end with &section=view-coupons as well.
Is it possible to hardcode query string params this way to work after reloading with my framework?
Search parameters are not part of the path. You can access those values through props.location
<Route
path={`/wptest2/wp-admin/options-general.php`}
render={props => {
// do something with props.location.search
The library https://www.npmjs.com/package/query-string is helpful here.

React pass an object to the route

I have this set up of my app in the ´app.js´ file I have my routers defined like this inside the render method
<HashRouter>
<StatusToast />
<Switch>
<Route
exact={true}
path="/"
render={props => (
<ImageScene{...props} title="Images & Information Search" />
)}
/>
<Route path="/case/:id/images" component={DetailsScene} />
</Switch>
</HashRouter>
from ImageScene on a table row click, I call a method like this:
this.props.history.push(/case/${id}/images)
this trigger a route and load DetailsScene where I can get the passed in id like this this.props.match.params.id all works without any problem so far.
My question is how can I pass a more than a string (the id) can I pass somehow the whole object to the route?
I have tried to do something like this for 2nd route instead of:
<Route path="/case/:id/images" component={DetailsScene} />
to set up on the ImageScene a method which can expose the selceted object, for now lets just do a simple one like:
export function getSelectedRow() {
return {
title: 'test'
}
}
and than set up the route like:
const object = getSelectedRow();
<Route path="/case/:id/images"
render={props => (<DetailsScene{...props} title={object.title} />
)}
/>
but I cannot make it work... any help would be graet, I'm totally new to react and the whole router.
you could add state to history.push (https://reacttraining.com/react-router/web/api/history) which is available for component being rendered (DetailsScene). Remember to wrap DetailsScene withRouter(...) to have history.location.state available in its props.

Resources