Navigate to new URL from within MemoryRouter structure - reactjs

When a user completes a booking process and navigate to the confirmed details view. I need the URL to change. This is proving to be difficult to work around as the routing in done through MemoryRouter which can neither read, nor write to the URL. I need to break one of the views out and have the browser navigate to this new view.
I have tried breaking out from one router and creating a second that would return based on the original URL, then tried the very hacky window.location and direct the url to the new router.
import React from 'react';
import { MemoryRouter, Route, Switch } from 'react-router-dom';
import {
Page,
StartScreen,
StoreSearch,
ServiceSelector,
StoreSelector,
OptionSelector,
AppointmentForm,
AppointmentDetails,
ConfirmationScreen,
ErrorScreen,
} from 'components';
import { WithPageTitle, ScrollToTop } from 'containers';
import { services } from 'utilities';
const NewAppRouter = () => {
return (
<MemoryRouter>
<ScrollToTop>
<Switch>
<Route exact path="/" component={StartScreen} />
<WithPageTitle>
{pageTitle => (
<Page pageTitle={pageTitle}>
<Route path="/zip" component={StoreSearch} />
<Route path="/services" component={() => ServiceSelector({
services: services.services,
withBackButton: true,
backTo: "/zip"
})} />
<Route path="/stores" component={StoreSelector} />
<Route path="/options" component={OptionSelector} />
<Route path="/form" component={AppointmentForm} />
<Route path="/details" component={AppointmentDetails} />
{/* <Route path="/confirmation" component={ConfirmationScreen} /> */}
<Route path="/error" component={ErrorScreen} />
</Page>
)}
</WithPageTitle>
</Switch>
</ScrollToTop>
</MemoryRouter>
)
}
const AppRouter = () => {
if(window.location.href="http://localhost:9998"){
return (
<NewAppRouter />
)
} else if (window.location.href="http://localhost:9998/confirmation") {
return (
<ConfirmRouter />
)
} else {
return console.error('Route Not Found')
}
}
export default AppRouter;

Related

Why is rendering the parent component and the child trying to enter the child component

Why is rendering the parent component and the child trying to enter the child component
"react-router-dom": "^6.0.1",
when I enter on the route:
http://localhost:3000/dashboard- the view work
http://localhost:3000/dashboard/employee - rendering dashboard and employee view (both views)
http://localhost:3000/dashboard/accounting - rendering dashboard and accounting view (both views)
Documentation:
https://reactrouter.com/docs/en/v6/getting-started/tutorial#nested-routes
index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
);
App.js
import AppRouter from "./routers/AppRouter";
function App() {
return (
<>
<AppRouter />
</>
);
}
export default App;
AppRouter.js
import { Route, Routes } from "react-router-dom";
import Navbar from "../components/template/Navbar";
import AccountingHomeView from "../components/views/accounting/AccountingHomeView";
import DashboardHomeView from "../components/views/dashboard/DashboardHomeView";
import EmployeeHomeView from "../components/views/employee/EmployeeHomeView";
import HomeView from "../components/views/public/HomeView";
import LoginView from "../components/views/public/LoginView";
const AppRouter = () => {
return (
<div>
<Navbar />
<Routes>
<Route path="/" element={<HomeView />} />
<Route path="dashboard" element={<DashboardHomeView />}>
<Route path="employee" element={<EmployeeHomeView />} />
<Route path="accounting" element={<AccountingHomeView />} />
</Route>
<Route path="/login" element={<LoginView />} />
</Routes>
</div>
);
};
export default AppRouter;
DashboardHomeView.js (with outlet)
import { Outlet } from "react-router-dom";
const DashboardHomeView = function () {
return (
<>
<h1>DashboardHomeView</h1>
<Outlet />
</>
);
};
export default DashboardHomeView;
component children Accounting
import React from "react";
const AccountingHomeView = function () {
return (
<div>
<h1> Accountin</h1>
</div>
);
};
export default AccountingHomeView;
I also initially found this a bit confusing, but with nested routes the "parent" route is considered more of a "layout" component in that it is always rendered when its path matches, and renders all its children routes into its outlet.
const AppRouter = () => {
return (
<div>
<Navbar />
<Routes>
<Route path="/" element={<HomeView />} />
<Route
path="dashboard"
element={<DashboardHomeView />} // <-- always matched/rendered at "/dashboard*"
>
<Route
path="employee"
element={<EmployeeHomeView />} // <-- conditionally matched/rendered
/>
<Route
path="accounting"
element={<AccountingHomeView />} // <-- conditionally matched/rendered
/>
</Route>
<Route path="/login" element={<LoginView />} />
</Routes>
</div>
);
};
const DashboardHomeView = function () {
return (
<>
<h1>DashboardHomeView</h1> // <-- always matched/rendered at "/dashboard*"
<Outlet /> // <-- conditionally matched/rendered children
</>
);
};
Nested-Routes
You may have noticed when clicking the links that the layout in App
disappears. Repeating shared layouts is a pain in the neck. We've
learned that most UI is a series of nested layouts that almost always
map to segments of the URL so this idea is baked right in to React
Router.
I believe what you are expecting is what is called an Index Route. It is what would be rendered on a "/dashboard" route when it isn't a layout/wrapper container.
Notice it has the index prop instead of a path. That's because the
index route shares the path of the parent. That's the whole point--it
doesn't have a path.
Maybe you're still scratching your head. There are a few ways we try
to answer the question "what is an index route?". Hopefully one of
these sticks for you:
Index routes render in the parent routes outlet at the parent route's path.
Index routes match when a parent route matches but none of the other children match.
Index routes are the default child route for a parent route.
Index routes render when the user hasn't clicked one of the items in a navigation list yet.
const AppRouter = () => {
return (
<div>
<Navbar />
<Routes>
<Route path="/" element={<HomeView />} />
<Route path="dashboard" element={<DashboardLayout />}>
<Route path="employee" element={<EmployeeHomeView />} />
<Route path="accounting" element={<AccountingHomeView />} />
<Route index element={<DashboardHomeView />} />
</Route>
<Route path="/login" element={<LoginView />} />
</Routes>
</div>
);
};
const DashboardLayout = function () {
return (
<div /* with any layout styling */>
.... other common layout content
<Outlet />
.... more possible common page content
</div>
);
};
const DashboardHomeView = function () {
return (
<>
<h1>DashboardHomeView</h1>
.... dashboard specific content
</>
);
};
How about using the exact prop for the parent Route. Like <Route exact path="dashboard" element={<DashboardHomeView />}>. This may solve the issue.

Show a navigation component on all routes except the root

I need to show a main navigation on all routes except the root route. If I was going to show on all routes I would do it like this:
class App extends Component {
render() {
return (
<Container className="App" maxWidth="lg">
<Grid className="app-container">
<MainNav />
<Switch>
<Route exact path="/" component={Home} />
<Route
exact
path="/some-other-route"
component={SomeOtherComponent}
/>
...
</Switch>
</Grid>
</Container>
);
}
}
I could make a wrapper component for all the other routes and put it there, but any solution I can think of to accomplish this just seems wrong and there's probably a better way. Is there a better way?
Maybe you can use this code.
The below code is inspired by nextjs page routing.
You just add your routes on ~/pages/ and it imports them dynamically.
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
function DynamicRoutes() {
return (
<BrowserRouter>
<Switch>
<Route
path="/"
render={({ history, location, match })=>{
const Page = React.lazy(()=>{
return import('./pages'+location.pathname).catch((e) => {
if (/not find module/.test(e.message)) {
return import("./pages/NotFound");
}
if (/Loading chunk \d+ failed/.test(e.message)) {
window.location.reload();
return;
}
throw e;
})
});
return (
<React.Suspense fallback ={<div>Loading...</div>}>
<Page />
</React.Suspense>
)
}}
/>
</Switch>
</BrowserRouter>
)
}
export default DynamicRoutes;

How to default to a route based on a URL parameter

I have a routing setup where if only 1 param is given, i.e /:id I want the router to always redirect to /:id/overview.
For example, if the user goes to /hello, I want them to be redirected to /hello/overview.
I've tried doing this like this:
<Switch>
<Route exact path="/" component={NoParam} />
<Redirect from="/:section" to="/:section/overview" />
<Route exact path="/:section/:pageName" component={GeneralOverviewPage} />
</Switch>
This causes an infinite re-render. I'm not sure how to achieve this redirect, and would really appreciate any help. Thanks.
EDIT=======
Now trying to do it like this:
const GeneralOverviewPage: FC<RouteComponentProps<GeneralOverviewPageProps>> = (
props
) => {
console.log(props);
return !props.match.params.pageName ? (
<Redirect to={props.match.params.section + '/overview'} />
) : (
<h1>{props.match.params.pageName}</h1>
);
};
export default GeneralOverviewPage;
and
<Route path="/:section" component={GeneralOverviewPage} />
<Route path="/:section/:pageName" component={GeneralOverviewPage} />
This means that /hello is now redirecting to /hello/hello/overview....
This will Help You around for understanding!
import React from 'react';
import { Switch, Route, Redirect } from 'react-router-dom'
const App = () => {
return (
<Switch>
<Route exact path="/" render={
() => console.log("Hi")
} />
<Redirect exact from="/:section" to="/:section/overview" render={
() => console.log("Hi 1")
} />
<Route exact path="/:section/:pageName" render={
() => console.log("Hi 2")
} />
</Switch>
)
}
export default App;
Your <Redirect from="/:section" to="/:section/overview" /> must be inside GeneralOverviewPage component.
if(!pageName){
return (<Redirect from="/:section" to="/:section/overview" />);
}

react-router-native <Redirect> renders blank page

The logic seems simple, though I've tried a half-dozen permutations to see if anything changes. I have no idea why react-router is behaving this way:
import React from 'react'
import { View, Text } from 'react-native'
import { observer, inject } from 'mobx-react'
import { NativeRouter, Link, Route, Redirect, Switch } from 'react-router-native'
import Welcome from './welcome'
import Tutorial from './tutorial'
import Plants from './plants'
#inject('store')
#observer
class Main extends React.Component {
render() {
const newUser = true //this.props.store.plants.length === 0
const home = newUser ? '/welcome' : '/plants'
return (
<Switch>
<Route path='/plants' component={Plants} />
<Route path='/tutorial' component={Tutorial} />
<Route path='/welcome' component={Welcome} />
<Redirect to={home} />
<Route path='/' component={Welcome} />
</Switch>
)
}
}
export default Main
The final 'welcome' should be unnecessary, but I've put it there to test: if I remove the then welcome does appear, so it's clearly the that's causing a blank page to render.
This is the render() method of the top-level component:
return (
<Provider store={store}>
<NativeRouter>
<Main />
</NativeRouter>
</Provider>
)
This is based on the example at https://reacttraining.com/react-router/native/guides/philosophy which shows a Switch, Route, and Redirect all being used without an enclosing Router:
const App = () => (
<AppLayout>
<Route path="/invoices" component={Invoices}/>
</AppLayout>
)
const Invoices = () => (
<Layout>
{/* always show the nav */}
<InvoicesNav/>
<Media query={PRETTY_SMALL}>
{screenIsSmall => screenIsSmall
// small screen has no redirect
? <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
</Switch>
// large screen does!
: <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
<Redirect from="/invoices" to="/invoices/dashboard"/>
</Switch>
}
</Media>
</Layout>
)
Use the NativeRouter as the topmost component in your Main component and it will work as expected.
#inject('store')
#observer
class Main extends React.Component {
render() {
const newUser = true //this.props.store.plants.length === 0
const home = newUser ? '/welcome' : '/plants'
return (
<NativeRouter>
<Switch>
<Route path='/plants' component={Plants} />
<Route path='/tutorial' component={Tutorial} />
<Route path='/welcome' component={Welcome} />
<Redirect to={home} />
</Switch>
</NativeRouter>
)
}
}

Sub routes at react router 4

I am having problems dividing my application and using several routers. I have a main router where it handles several small applications and in each mini application I want to manage its opportune routes. What am I failing?
What I want to do is when I receive the data of the request, redirect me to a new screen but I can not get it. Can anybody help me? Thank you
Example https://stackblitz.com/edit/react-c2tkgf?file=Hello.js
Routes.js
import { BrowserRouter } from 'react-router-dom'
import React from 'react'
import { Switch, Route } from 'react-router-dom'
import { AuthenticatedRoute } from 'components/authenticated-route'
import Clients from 'components/clients'
import { Login } from 'components/login'
import Home from './Home/Home'
const Routes = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />} />
<Route exact path="/clients" component={Clients} />
<Route exact path="/login" component={Login} />} />
</Switch>
</BrowserRouter>
)
}
export default Routes
Clients.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Dashboard from './Dashboard/Dashboard'
import { Redirect, Route, Switch } from 'react-router-dom'
class Clients extends Component {
render() {
return (
<div>
<SearchCustomers />
{this.props.customer.token ? (
<div>
<Switch>
<Route path={`/clients:${this.props.customer.id}/dashboard`} component={Dashboard} />
</Switch>
<Redirect to={`/clients:${this.props.customer.id}/dashboard`} />
</div>
) : null}
</div>
)
}
}
const mapStateToProps = state => {
return {
customer: state.customer,
}
}
export default connect(mapStateToProps)(Clients)
In your Routes component you have:
<Route exact path="/clients" component={Clients} />
<Route exact path="/login" component={Login} />} />
since you have exact on there, those components will only be rendered when at exactly /clients or /login. in your built components, once you change the path, your parent component no longer renders, therefore nothing inside those components will render. remove the exact from your Routes:
<Rout path="/clients" component={Clients} />
<Rout path="/login" component={Login} />} />

Resources