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
"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.
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;
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" />);
}
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>
)
}
}
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} />} />