React router paths - reactjs

So I'm trying to display a 404 page if the path in my web app is undefined or nothing is rendered due to something having been deleted. For example, users can create books and the books id becomes the path to that page. But if a user deletes that book, the path will be undefined. So, if that happens id like to be able to make sure that any other user that tries to go onto that page is shown a 404 message.
For example, a link could be
'/home/undefined'
or
'/undefined/books'

To create any sort of "404 route", you simply have to add a route that matches any URL, and use it in a switch tag along with the valid routes. For instance:
<Switch>
<Route path ="/home" component={Home} />
<Route path ="/home/book" component={Book} />
<Route path ="/" component={FourZeroFour} />
</Switch>
You can also achieve the same effect by not using a Switch, and instead using exact on every route except the final one:
<Route path ="/home" component={Home} exact />
<Route path ="/home/book" component={Book} exact />
<Route path ="/" component={FourZeroFour} />
But what if you have a route that potentially matches both invalid and valid URLS, for instance:
<Route path ="/home/book/:bookId" component={SpecificBook} />
(where :bookId could be undefined)
In that case, you'll instead want to check the URL parameter's validity inside your component. For instance, your SpecificBook component might do the following:
const SpecificBook = ({ bookId } ) => {
if (bookId === 'undefined') return <Redirect to="/404" />;
// rest of component
}
You could also use history.push, but the Redirect tag will do more than just add a history entry; it will simulate a (server-side) redirect.

I will check the book_id first and the redirect if not found.
import {useHistory} from "react-router-dom"
function App() {
const history = useHistory();
if (!bookId) {
history.push("/404");
return null;
}
return <></>
}

Related

React redirect from a route to another route and pass the id to the new one

In short words, I want to know why this line does not work:
<Route path="/game/prepare/:id" element={ <Redirect to="/game/live/:id" /> } />
More description:
I have this url: www.mysite.com/game/prepare/1234 and I want it to instantly redirect to www.mysite.com/game/live/1234.
The implementation doesn't work because you are redirecting to the literal path string "/game/live/:id", the original id path parameter value is dropped. Redirect also ins't a valid react-router-dom#6 component. You should use the Navigate component, which is the spiritual successor to the RRDv5 Redirect component.
<Route
path="/game/prepare/:id"
element={<Redirect to="/game/live/:id" />}
/>
You'll need to create a custom component that can read the current route path params and render the appropriate redirection.
Example:
import { Navigate, useParams } from 'react-router-dom';
const RedirectToLiveGame = () => {
const { id } = useParams();
return <Navigate to={`/game/live/${id}`} replace />;
};
<Route
path="/game/prepare/:id"
element={<RedirectToLiveGame />}
/>

How to ignore last url part with react router?

I want to use the react router to show a page according to the first part of the URL, because the last part of the URL is holding an ID. How can I read out the last part, now I'm using string.split("/").pop()
URL
http://localhost:3003/profile/313a2333
Router
<Routes>
<Route path="/profile" element={<Profile/>} />
</Routes>
Define your Route like the following:
<Route path="/profile/:user_id">
Then you can get 2nd parameter in Profile component.
import { useParams } from 'react-router-dom';
const params: any = useParams();
const user_id = params.user_id;
If you don't care about the second path segment and only want to match according to the first segment then use a "*" wildcard matcher.
<Routes>
<Route path="/profile/*" element={<Profile/>} />
</Routes>
If you do need to capture this second path segment value, then provide a param name for it.
<Routes>
<Route path="/profile/:id" element={<Profile/>} />
</Routes>
Use the useParams hook to access route path params in the routed components.
const { id } = useParams();

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 pass query params in every single route path in react?

I'm trying to implement React Router with query params like so http://localhost:3000/login?Id=1, I was able to achieve it only for my login route that too if I put path as http://localhost:3000/ which then redirects , however, I want to implement across the application. It matches nomatch route if I implement on other routes. This is how my index.js looks like, Can someone guide me how can i go about implementing all routes path including query params ?.
ReactDOM.render(
<BrowserRouter>
<Switch>
<Route
exact
path={`/`}
render={() => {
if (!store.getState().login.isAvailable) {
return <Redirect to={`/login?Id=${Id}`} />
} else {
return <Dashboard />
}
}}
/>
<Route exact path={`/login`} component={Login} />
<Route exact path={`/signup`} component={SignUp} />
{Routes.map((prop, key) => (
<Route path={prop.path} key={key} component={prop.component} />
))}
<Route component={NoMatch} />
</Switch>
</BrowserRouter>,
document.getElementById('root')
)
There are two ways about to accomplish what you want.
The most basic way would be on each "page" or root component of each route, handle the parsing of query params.
Any component that is the component of a Route component, will have the prop location passed to it. The query params are located in location.search and that will need to be parsed. If you are only worried about modern browsers, you can use URLSearchParams, or you can use a library like query-string, or of course, you can parse them yourself.
You can read more in the react-router docs.
The second way of doing this, really isn't that different, but you can have a HOC that wraps around each of your "pages" that handles the parsing of the query params, and passes them as a list or something to the "page" component in question.
Here's an example of the basic way using URLSearchParams:
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
// this is your "page" component, we are using the location prop
function ParamsPage({ location }) {
// you can use whatever you want to parse the params
let params = new URLSearchParams(location.search);
return (
<div>
<div>{params.get("name")}</div>
// this link goes to this same page, but passes a query param
Link that has params
</div>
);
}
// this would be equivalent to your index.js page
function ParamsExample() {
return (
<Router>
<Route component={ParamsPage} />
</Router>
);
}
export default ParamsExample;
EDIT: and to clarify, you don't need to do anything on your index.js page to make this work, the simple Routes you have should work fine.

How to save bad invalid URLs that were typed in?

I am having a react-redux app and react-router v4 inside of app
Is there a way to catch all invalid URLs that were entered and save them to an array, like so ['https://mysite.co/progects', 'https://mysite.co/sometypo', 'https://mysite.co/something']?
And then I want to send that data to server for building some redirects and some sitemap
Currently I have this:
<Switch>
{/* <Route path='/blog' exact component={Blog} /> */}
<Route path='/projects/:id' component={ProjectDetails} />
<Route path='/career/:id' component={CareerDetails} />
<Route path='/apply-for-job' render={(props) => (
<ModalWindow
{...props}
modalHeader='Apply form'>
<ApplyForm history={props.history} />
</ModalWindow>
)} />
<Route exact path='/' component={withScrollPreservation(LandingPage)} />
<Route component={NoMatch} />
{/* <Route component={withScrollPreservation(LandingPage)} /> */}
</Switch>
In your NoMatch component, you can have the logic to update unmatched/incorrect urls
class NoMatch extends React.Component {
componentDidMount() {
const { addNoMatchUrl } = this.props;
// you might want to handle the duplicate url logic here too in addNoMatchUrl method
addNoMatchUrl(this.props.location.pathname);
}
componentDidUpdate(prevProps) {
const { location, addNoMatchUrl } = this.props;
if (location.pathname !== prevProps.location.pathname) {
addNoMatchUrl(location.pathname);
}
}
render() {
// render logic
}
}
export default connect(null, {addNoMatchUrl});
If you want, say, to redirect someone, who typed '/progects' to '/projects' - well, that's nice UX, but your Switch block will be cluttered with tens of possible invalid urls.
As I see it, maybe you should add <Redirect to='/main' /> at the bottom of your Switch so any invalid url gets redirected to Main component (or whichever you have) or to 404-Component.
If you still want to gather them, then instead of redirecting to Main or 404 Component, send them to specific Error component, where you can get the link via this.props.history.location and handle that link further in the component: send to server, set that url in local/session storage, etc.
Note that you'll need a way to store that data someplace which won't get cleared on unmounting.
In order to send users to that route you'll need to place that at the bottom of your Switch.
<Switch>
...{your routes}
<Route component={Error} />
</Switch>
So actually all you need to do is handle those urls in your NoMatch component, like so
const path = this.props.history.location;
axios.post('your api', path);

Resources