React Router: Navigate without changing the URL sometimes - reactjs

In React Router V4, I have a NotFoundScreen component set up to current standards.
It will catch all completely unknown routes fine, but I'm wondering if there is a way to navigate to it without changing the browsers URL. The rest of the app uses typical history.push() URL changing behavior, so I'm not sure I can use the memoryHistory described here: React Router Without Changing URL
To highlight my problem, if a user does not exist, it would be convenient programmatically navigate to NotFoundScreen without changing the users incorrect URL so they could catch a typo or use the browser's back button.
import React from 'react'
import { BrowserRouter, Route, Switch } from "react-router-dom";
import UserScreen from './UserScreen';
import HomeScreen from './HomeScreen';
import NotFoundScreen from './NotFoundScreen';
const Routes = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path='/user/:userID' component={UserScreen}/>
<Route exact path='/' component={HomeScreen}/>
{/*NotFoundScreen will pick up all unknown routes. I want
to navigate here without changing the URL*/}
<Route component={NotFoundScreen}/>
</Switch>
</BrowserRouter>
);
};
export default Routes;

Related

Is there a way to update history location of app registered in single spa when url changes?

I have two apps within single-spa, one in React and other in Vue.
The React app uses history library for navigation. Below given are my React app files:
history.js
import { createBrowserHistory } from 'history'
export const history = createBrowserHistory({
basename: '/myapp,
forceRefresh: true
})
App.js
import React, {Component} from 'react';
import { Router, Switch, Route } from 'react-router-dom';
import history from ‘../history.js’;
class App extends Component {
constructor(props){
super(props);
}
render(){
return (
<Router history={history}>
<Switch>
<Route exact path="/user" component={User} />
<Route exact path="/" component={Home} />
</Switch>
<Router />
)
}
}
I face an issue when:
I’m at the path https://localhost:3000/myapp/user of React app and I switch to the Vue app (using navbar menu).
Now when I switch back to React app, the url shows https://localhost:3000/myapp which ideally should load my Home component.
But now the history still has the old location (https://localhost:3000/myapp/user) which then loads the old User component.
Is there a way to update history when the url changes?
I didn't find how to change history, but I want you to fix some grammar errors. Meybe fixing errors can help you.
history.js
export const history = createBrowserHistory({
basename: '/myapp, // basename: '/myapp',
forceRefresh: true
})
App.js
<Switch>
<Route exact path="/user" component={User} /> // <Route path="/user" component={User} />
<Route exact path="/" component={Home} />
</Switch>
I think this boils down to getting to routing mechanisms to work together. This is an interesting problem and I have two ideas that could work:
Idea 1: refresh the page
If you're okay with a full reload, refreshing the browser whenever you switch between Vue and React would probably reset the state in both routers so that they both read the initial state they loaded on and continue.
I would do this by assigning window.location.href = '/myapp' inside any event handler that would cause the transition to React. It should reload the browser and navigate to that page. This should work if your react app is loading at that path.
Idea 2: Use createMemoryHistory and sync it with Vue's router
Instead of feeding react-router a "browser router", you can instead pass it a "memory router". As the name implies, the memory router holds state in memory (instead of using the History API).
I'm thinking you can wire up the events from Vue's router to react-router's memory router and get them in sync like that. I think you can call history.push inside of Vue's global after hooks.
Something like this:
// 🔴 I have not tested this, take this as inspiration
const router = new VueRouter({/* ... */});
const history = createMemoryHistory({/* ... */});
router.afterEach(({fullPath}) => {
history.replace(fullPath, {fromVueRouter: true});
});
history.listen(location => {
if (location.state?.fromVueRouter) return;
router.replace(location.pathname);
});

How to detect 404 routes (NoMatch) when doing SSR with "react-router-dom" without using <Redirect>?

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:

Redirect in React Router 5 is successfully redirecting, but not rendering the child component

I have changed the routes in my app, and in case any users have bookmarked urls to the old routes I have added some redirects to the new routes. Most of them are working fine, however this one is not -
App.tsx
import { Router } from 'react-router-dom';
import Routes from './Routes';
import history from './history';
const App: FunctionComponent = () => (
<Router history={history}>
<Routes />
</Router>
);
export default App;
RouteSwitch.txs
import React, { FunctionComponent } from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
const RouteSwitch: FunctionComponent = () => {
return (
<Switch>
<Redirect exact from="/documents" to="/documents/list" />
<Route exact path="/documents/list">
<DocumentsContainer />
</Route>
</Switch>
);
};
export default RouteSwitch;
The redirect from /documents to /documents/list works, however the DocumentsContainer does not get rendered. If I directly request /documents/list then it renders fine. It's as if <Switch> finds its first match (the Redirect) and then decides its job is done. I tried adding the push prop to the Redirect but it didn't make a difference.
My example is very similar to the one given on the React Training site - https://reacttraining.com/react-router/web/api/Redirect/from-string
Thoughts?
Problem solved - the DocumentsContainer component was actually rendering, but just not showing anything due to a quirk with how the use of the redirect was causing a loader count to be incremented by one (but not decremented) resulting in the loader count not returning to 0 to allow document data to be loaded from Redux and the content to be rendered.

View section does not load on click on navigation links but when URL change manually on the browser then page load working according to url

I am new in react js. So it is a hard problem for me to solve it.
View section does not load on click on navigation links but when URL change manually on the browser then page load working according to url
import AboutUs from './AboutUs';
import News from './News';
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
<Router history={history}>
<Switch>
<Route exact path="/" component={News} />
<Route exact path="/about-us" component={AboutUs} />
</Switch>
</Router>
I have done lots of the way using the different code using the withRouter function but I can not able to solve this issue.
Please help me.
I have fixed my issue. And the issue was in the News and AboutUs components where we have defined the Router inside the router so in the total two-time same router was called in the system. So I have just removed the router related codes from the News and AboutUs component.
You can not define the multiple routers for the same pages with
multiple times.

React-router having trouble display each of the components on its own

I'm migrating to ReactRouter v4 and it's just been proving a hassle to me. I have a file in app.js in which i'm trying to setup a simple routing system.
import React, { Component } from 'react';
import { Router, Route, IndexRoute, hashHistory, Redirect } from 'react-router';
import LoginPage from './LoginPage';
import NavigationBar from './NavigationBar';
class App extends Component {
render() {
return (
<Router history={history}>
<Route exact path='/' component={NavigationBar}></Route>
<Route exact path='/login' component={LoginPage}></Route>
</Router>
);
}
}
The code compiles perfectly well, but for some reason on the browser, the navigation bar is shown instead of the login page when the url is localhost:3000/login
The image of the nagivation bar showing instead of the login
I intended the login to be shown at /login and the navigation bar to be shown at /, but instead the navigation bar is shown at both /login and /.
I've been following the examples on the react-router training guide here, and I can't seem to figure out what I'm doing wrong.
Maybe it's just a typo in your post here, but you're extending Compoent instead of Component. I would guess you'd see worse issues than getting the wrong component if that was happening in your code.
I prefer BrowserRouter (I don't have to support legacy browsers), but I got your code working like this:
import React, { Component } from 'react';
import { Route } from 'react-router';
import { HashRouter as Router } from 'react-router-dom';
import LoginPage from './LoginPage';
import NavigationBar from './NavigationBar';
class App extends Component {
render() {
return (
<Router>
<div>
<Route exact path='/' component={NavigationBar}></Route>
<Route exact path='/login' component={LoginPage}></Route>
</div>
</Router>
);
}
}
You'll have to install react-router-dom for this code to run.
Visiting http://localhost:3000/#/login, I get the Login component. If you change HashRouter to BrowserRouter, you can see your component render at http://localhost:3000/login.

Resources