I'm currently working on a notes app in React and I've built a 404 page and now if I go to a route that doesn't exist it shows a 404 page
for example, if I go to the route /get-the-note/ I get a 404 page.
But the problem is that I have a route:
<Route exact path="/single-note/:id" component={Singlenote}></Route>
which needs an id and if I put the correct id like I have a note with id no 2 and if I go to the route /single-note/2 it works but if I put an id that doesn't exist like if I put an id 120 which doesn't exist and I go to the route /single-note/120 I get a plain page instead of a 404 page. I want it show the 404 page even if the id is incorrect
here is my app.js file:
import "./App.css";
import Homepage from "./components/homepage.component";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Singlenote from "./components/singlenote.component";
import PageNotFound from "./components/404_page";
function App() {
return (
<Router>
<Switch>
<Route exact path="/" component={Homepage} />
<Route exact path="/single-note/:id" component={Singlenote}></Route>
<Route path="*"><PageNotFound /></Route>
</Switch>
</Router>
);
}
export default App;
and here is my 404 page:
import React from 'react'
const PageNotFound = () => {
return (
<div id="wrapper">
<img src="https://i.imgur.com/qIufhof.png" />
<div id="info">
<h3>This page could not be found</h3>
</div>
</div >
)
}
export default PageNotFound
The router has no way of knowing which IDs do and do not exist.
It's up to your Singlenote to render 404 content if there is no such ID.
For the last route in the Switch, you don't need the path at all, by the way.
<Route component={PageNotFound} />
The approach I suggest for this is by modifying your Singlenote component, with a useEffect hook, that checks if the id provided exists or not, only once on mount (useEffect(() => { code for check }, []);). In the event that the id does not exist, you can redirect the execution to your 404 component, by using history (e.g. history.push('*'), where * is used as the path to redirect to, you could use something else that is not covered in the routes, so it will go to 404).
I have to add that this is not a good approach, I would personally go with a notification or other indicator displayed when accessing your Singlenote component with an id that does not exist (again with useEffect), which informs the user that no results or data were found for the given id.
Related
I am trying to convert my HTML website into react , I've created header, footer and home page. Now I want to navigate to inner page on click of image on home page.
I used router and switch too but unable to call that page.
I am getting an error 'Listing' is not defined (page name is Listing)
I am adding the code too
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
import Listing from './Listing'
The above I added on top of my home page
<Link to='/app/listing'>
<a href="palm-valley.html" class="property-slider-img bg-xs" style={{backgroundImage: "url(" + "images/header/palm-valley-header.jpg" + ")",}}>
</a>
</Link>
This is how I added link
<Switch>
<Route path="/listing"><Listing/></Route>
</Switch>
And this is how I used switch
You are importing Listing as Inner, so you need to use <Inner />.
Change your import to
import Listing from './Listing'
Also to does not match any path, so change
<Link to='/listing'> ... <Link>
You don't need to but a <a></a> in your <Link></Link> you can add a className on the <Link></Link> if you want but it's not important
I saw that you have import Router but make sur that you use it like that:
<Router>
<Switch>
<Route/>
</Switch>
</Router>
Then you shouldn't use the <Route/> like that, here an exemple:
<Route
exact
key='/path'
path='/path'
render={(routeProps) => (<ComponentToRender/>)}
/>
And if they say that 'Listing' is not defined this is because you have not import it
I'm using react-router-dom and I'm implementing SSR (Server Side Rendering) to my web app.
I'm having trouble on how to handle 404 routes.
My goal:
Whenever someone visits a /non-existent-route on my web app. I DON'T want to redirect them. They should stay on that very same route, and they should get HTTP status 404 along with the 404 Page.
This should be the sequence:
REQ GET /non-existent-route
RES 404 /non-existent-route along with the HTML for the 404 Page
Example:
A user tries to access a path that does not match any route.
It should be "captured" by my last <Switch> <Route> and the Page404 gets rendered. Just like this example from the official docs.
<Switch>
<Route exact path={"/route1"} component={Component1}/>
<Route exact path={"/route2"} component={Component2}/>
<Route path={"*"} component={Page404}/>
</Siwtch>
When I'm rendering it using renderToString from react-dom/server, this is what I do:
From: react-router docs
If I was redirecting it to a 404, I could use the context.url to detect that a redirect has happened (like the example from the doc below). But I don't want to redirect. I want to return the 404 status on that very same route. Therefore I cannot use context.url to detect the 404 route.
QUESTION
How to detect that no specific route was matched and the Page404 was rendered when I'm not redirecting from the render?
Just found out how to do it.
From the official docs we get that:
You can add information to the staticContext. And that information will be added to the context object even if you don't <Redirect/> from the render.
Here is a CodeSandbox with this example:
App.tsx
import * as React from "react";
import { Switch, Route } from "react-router-dom";
import "./styles.css";
export default function App() {
return (
<Switch>
<Route
path={"*"}
render={({ staticContext }) => {
if (staticContext) {
staticContext.statusCode = 404;
}
return <div>WHATEVER</div>;
// return <Redirect to="/404" />;
}}
/>
</Switch>
);
}
index.tsx
import * as React from "react";
import { StaticRouter } from "react-router-dom";
import { renderToString } from "react-dom/server";
import App from "./App";
// ON THE SERVER
const context = {};
const html = renderToString(
<StaticRouter location={"/"} context={context}>
<App />
</StaticRouter>
);
console.log(JSON.stringify(context));
This is the result (logged from index.tsx). As expected:
Trying to learn react and I created a Navbar with a simple list of links that point to routes in App.tsx. As it's working currently, all my components display the Navbar. Including the Login component, which is not what I want.
The behavior I'm looking for would display the login component without the Navbar when the app is launched, so path "/". And then once they click the login button, it would "log them in" and display a default component with the Navbar and allow them to move around the app.
Before I get into the state part, I need to know how I can go about achieving this first. I tried re-arranging the Navbar position in App.tsx to no avail (the Navbar contains links, so it has to be within a Router). All the other routes need the navbar as well, so it makes sense to put it first, except for login (or any other page I don't want to include the navbar).
I'll leave out my Navbar component since it just has links. But my App component looks like this, which hopefully is enough to explain what I'm missing:
App.tsx:
import * as React from "react";
import { useState } from "react";
import "./index.css";
import {
BrowserRouter as Router,
Switch,
Route,
Redirect,
} from "react-router-dom";
import Navbar from "./components/Navbar";
import VisitLog from "./components/VisitList";
import Dashboard from "./components/Dashboard";
import Profile from "./components/Profile";
import Login from "./components/Login"
export default function App() {
const [loggedIn, setLoggedIn] = useState(false);
return (
<div>
<Router>
<Navbar />
<Switch>
<Route exact path="/" />
<Route path="/profile" component={Profile} />
<Route path="/visits" component={VisitLog} />
<Route path="/dashboard" component={Dashboard} />
<Route path="/login" component={Login} />
</Switch>
</Router>
</div>
);
}
Do I need to rework my navbar and routes completely or is there a relatively simple way to make exceptions to which components see the navbar?
Also, I'm open to other ideas to achieve a similar effect. It's just a standard practice in any website to display login page if not logged in, but I wasn't able to find much for my navbar display purposes.
Pretty simple. I must be missing something.
Thanks!
What if you avoid render navbar if user is not logged in and redirect it to '/login'path.
return (
<div>
<Router>
{lodggedIn ? <Navbar /> : <Redirect to="/login"/>}
<Switch>
<Route path='/login' component={() => <Login setLogin={(islogged)=> setIslogged(isLogged)}/>}/>
</Switch>
</Router>
</div>
);
And in your Login component you pass the result of the login attempt to the setLogin function in props.
example:
function attemptLogin(){
let result = get login results
this.props.setLogin(result);
}
Backstory:
I'm building this simple application and i want to make it have pretty urls with react-router v4.
I've got 3 main components: {Home}, {List}, {Hotel}:
{Home} Component - a simple component where i can select country, datefrom and dateto and route myself to {List}.
Current path example: http://url.com/
{List} Component - A heavy component with a cards full of hotels with links to /hotel/ route.
Current path example: http://url.com/list/country/?datefrom=2019-01-01?dateto=2020-01-01
{Hotel} Component - Most far part of the app, heavy component with hotel information and list of prices split by day.
Current path example: http://url.com/hotel/country/hotel-name/?datefrom=2019-01-01?dateto=2020-01-01
The problem: What i'm trying to do is replace /list/ and use /hotels/ for a {List} component, so i would have somewhat of a hierarchy in url structure.
But once i tried to change /list/ -> /hotels/ for a {List} component route, my whole app breaks and i'm greeted with lots and lots of errors from {Hotel} component, this happens when i try to route myself from {Home} component to {List}.
What i've tried: i've tried to use <Switch> component, it makes {Home} -> {List} route work, but when i try to route myself any further to a {Hotel} component, it actualy render at the bottom of the page and doesn't replace my {List}
Here's my app.js file that gives me strange behavior...
import React from "react";
import { Route, BrowserRouter as Router } from "react-router-dom";
import Home from "./Home";
import List from "./List";
import Hotel from "./Hotel";
import './styles/app.scss';
class App extends React.Component {
render() {
return (
<Router>
<div className="app">
<Route path="/" component={Home} exact />
<Route path="/hotels/:selectedCountry/:datefrom?:dateto?" component={List} />
<Route path="/hotels/:selectedCountry/:selectedHotel:datefrom?:dateto?" component={Hotel} />
</div>
</Router>
);
}
};
export default App;
Hotel and List refer to the same resource, a hotel, but Hotel refers to a single hotel, i.e. is singular, while List refers to an index of hotels, i.e is plural. Usually, routing is done by having a singular and a plural route.
Example taken from GitHub:
URL of a list of commits: https://github.com/facebook/react/commits
URL of a single commit: https://github.com/facebook/react/commit/ec6691a6
In your case it would be:
<Route path="/hotels/your-search-params here" component={List} />
<Route path="/hotels/:hotelId:" component={Hotel} />
Your route definition is not correct.
e.g. take a look at the route path /hotels/:country/:id/:fromDate/:toDate, so a url like this /hotels/usa/123/2019-01-01/2019-12-30 will map correctly to route params and you can access these values in your component like this.props.match.params.id etc. but if you want to take the query params approach then you need to add & sign in between two params like /hotels?country=usa&id=123&fromDate=2019-01-01&toDate=2019-12-30 and then you will access it from window.location.href and parse the query parameters or use npm package like query-params to parse. In this scenario your Route path will be just /hotels
I'm new with React. I'm using react-router-dom.
import React from 'react';
import { Router, Route, Switch, Link } from 'react-router-dom';
import Home from './components/home';
import Login from './components/login';
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="app">
<Link to='/'>Home</Link>
<Link to='/login'>Login</Link>
<Switch>
<Route exact path='/' component={Home}/>
<Route path='/login' component={Login}/>
</Switch>
</div>
);
}
}
export default App;
I'm using this code everything work fine, but when I go to localhost:8080/login directly via de url I get a error Cannot GET /login but it goes well through a link <Link to='/login'>Login</Link>.
how can I fix it?
So this is often a tricky thing I deal with in regards to navigating with react.
By navigating directly to /login your React App hasn't actually rendered the highest parent or root and thus there really isn't a login component to find and render. Here's what I do when working with logins and home
<Route exact path="/" component={() => this.props.auth ? <DashboardContainer/> : <Redirect to="/" />}/>
This says if there isn't any auth (or local storage, or cookies, or whatever you're using to log them in) redirect back home or to / so they can click /login.
Basically you need your highest level component to mount virtually before you can do anything else. You can also make /login your landing page, so in this case merge / and /login. But the above example is modularized and dynamic.