I'm passing an object to a class component and want to have that component open in a different route. The routing works, but all the time the props are undefined in the child component unless I move the <Route path=''...> line before every other component. The props work, but the page display is not correct.
PARENT
import React, { Component } from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Header from "./Header";
import DarbaiLT from "./DarbaiLT";
import AnObject from "./AnObject";
let clickeddiv = ''
class App extends Component {
onObjectClick = (clickeddivffromdarbai) => {
clickeddiv = clickeddivffromdarbai;
console.log("clickeddiv: ", clickeddiv);
};
*//clickeddiv is data coming from DarbaiLT component*
};
render() {
return (
<Router>
<div>
<Switch>
<Route path="/object/:id" exact component={AnObject} />
<Route path="/about" exact component={About} />
<Route path="/contacts" exact component={Contacts} />
<Route path="/partners" exact component={Partneriai} />
<DarbaiLT onObjectClick={this.onObjectClick} />
<AnObject dataforComponent={clickeddiv}/> //when this line is the last, it's not working
</Switch>
<Footer />
</div>
</Router>
);
}
}
export default App;
CHILD
import React, { Component } from "react";
class AnObject extends Component {
constructor(props) {
super(props);
}
render() {
return (
<>
<div onClick={() => console.log(this.props.dataforComponent)}>
<img src='../smth/pic.jpg' width="100%" />
</div>
</>
);
}
}
export default AnObject;
if I move the line to the top, passing of props works, but then all pages show only the AnObject, and doesn't render the About, Contacts and so on...
<Router>
<div>
<Header />
<Slides />
<Switch>
<AnObject stateforyou={clickeddiv}/> //if the line is here, routing doesn't work
<Route path="/object/:id" exact component={AnObject} />
<Route path="/about" exact component={About} />
<Route path="/contacts" exact component={Contacts} />
<Route path="/partners" exact component={Partneriai} />
<DarbaiLT onObjectClick={this.onObjectClick} />
</Switch>
<Footer />
</div>
</Router>
the documentation of React Router states that: "All children of a < Switch > should be < Route >".
Using the react-router Switch is like using switch case statement of javascript, whenever the link is matched to the route, the passed component gets rendered. Your problem here, however, is how to pass props to the rendered component which is done this way:
<Switch>
<Route path="/object/:id" exact component={() => <AnObject stateforyou={clickeddiv}/> } />
<Route path="/about" exact component={About} />
<Route path="/contacts" exact component={Contacts} />
<Route path="/partners" exact component={Partneriai} />
</Switch>
I'm not sure I totally understand the issue. But when you use <Switch> in the react-router, it will render only the first match.
You have a switch set up like this:
<Switch>
<Route path="/about" exact component={About} />
<DarbaiLT onObjectClick={this.onObjectClick} />
<AnObject dataforComponent={clickeddiv}/>
</Switch>
This means that if the visitor is at the url /about, it will render only the About component and nothing else. If you want to be able to render multiple components simultaneously as siblings, remove the <Switch>...</Switch>.
Related
So I'm learning React and building an app with multiple pages, I made a Routes file which looks like this:
import 'swiper/swiper.min.css';
import React from "react";
import { Route, Routes } from "react-router-dom";
import Home from "../pages/Home";
import Catalog from "../pages/Catalog";
import Detail from "../pages/Detail";
const Router = () => {
return (
<Routes>
<Route
path='/:category/search/:keyword'
component={Catalog}
/>
<Route
path='/:category/:id'
component={Detail}
/>
<Route
path='/:category'
component={Catalog}
/>
<Route
path='/'
exact
component={Home}
/>
</Routes>
);
}
And App.js looks like this:
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Header from './components/header/Header';
import Footer from './components/footer/Footer';
import Router from './config/Router';
function App() {
return (
<BrowserRouter>
<Routes>
<Route render={props =>{
<>
<Header {...props}/>
<Router/>
<Footer/>
</>
}}/>
</Routes>
</BrowserRouter>
);
}
export default App;
As you see, I have a browser router and Route which passes props to a component(as I understood) but for some reason the components don't display on the page(original components just have with their name inside of them, but they don't display in App.js).
And my console also says:
No routes matched location "/"
In routes.jsx file. I'm guessing it should lead to main page, but for some reason the route doesn't match and components in App.js don't display.
In Version 6.0.0 there is not any component prop in Route. It has been changed to element. So you need to change your Router to :
const Router = () => {
return (
<Routes>
<Route
path='/:category/search/:keyword'
element={Catalog}
/>
<Route
path='/:category/:id'
element={Detail}
/>
<Route
path='/:category'
element={Catalog}
/>
<Route
path='/'
exact
element={Home}
/>
</Routes>
);
}
As you've said you're using react-router-dom 6.0.2, and it seems that the tutorial you are following is for the older version (5?). There were some breaking changes in version 6.
You need to change your Router component to use element instead of component:
const Router = () => {
return (
<Routes>
<Route path="/:category/search/:keyword" element={<Catalog />} />
<Route path="/:category/:id" element={<Detail />} />
<Route path="/:category" element={<Catalog />} />
<Route path="/" exact element={<Home />} />
</Routes>
);
};
and also your App component seems to be getting in the way with the nested route.
I think it can be simplified to:
function App() {
return (
<BrowserRouter>
<>
<Header />
<Router />
<Footer />
</>
</BrowserRouter>
);
}
You can see a working demo on stackblitz
I am making a project and getting some problem in managing routes. My Frontend is divided in two parts. One For the Client side and onother is the admin-panel for handling the Client side. For example if I add some Blog from admin-panel then it shows on Client-side. Admin-Panel is for my team to handle the website. Suppose Users will visit on my website at "www.mywebsite.com' and I want that if I enter "www.mywebsite.com/admin" then Admin-panel and Admin-components should open instead of Nav-Components.
How Do I achieve this conditional routing?
Here is the App.js
import React, { Component, useEffect, useState } from "react";
import { Route, Switch } from "react-router-dom";
import Landing from "./components/Landing";
import Home from "./components/Home";
import About from "./components/About";
import Teams from "./components/Team";
import Events from "./components/Events";
import NotFound from "./components/NotFound";
import Blog from "./components/Blog";
import ContactUs from "./components/Contact";
import ComingSoon from "./components/ComingSoon";
import Navbar from "./components/Navbar";
import EventInfo from "./components/EventInfo";
import AdminNavbar from "./admin-panel/AdmiNavbar";
import Login from "./admin-panel/Login";
import Eventadd from "./admin-panel/Eventadd";
import Blogadd from "./admin-panel/Blogadd";
import Dashboard from "./admin-panel/Dashboard";
const NavComponents = () => {
return (
<>
<Switch>
<Route path="/home" exact component={Home} />
<Route path="/about" exact component={About} />
<Route path="/events" exact component={Events} />
<Route path="/team" exact component={Teams} />
<Route path="/blog" exact component={Blog} />
<Route path="/contact" exact component={ContactUs} />
<Route path="/comingsoon" exact component={ComingSoon} />
<Route path="/eventinfo/:eventName" exact component={EventInfo} />
<Route component={NotFound} />
</Switch>
</>
);
};
const AdminPanel = () =>{
return(
<>
<Switch>
<Route path="/admin/Eventadd" exact component={Eventadd}/>
<Route path="/admin/Blogadd" exact component={Blogadd}/>
<Route path="/admin/DashBoard" exact component={Dashboard}/>
<Route component={NotFound} />
</Switch>
</>
);
};
class App extends Component {
render() {
return (
<>
{window.location.pathname=="/"?"": <Navbar />}
<Switch>
<Route path="/" exact component={Landing} />
<NavComponents />
</Switch>
</>
);
}
}
export default App;
I assume if users enter your website through "www.mywebsite.com/admin" link, you want to re-route them to "/admin/DashBoard" route? The admin dashboard doesn't show because, it only returns the route with EXACT match. It's possible to
A) Add an additional path to handle the routing
<Route path=["/admin/DashBoard", "/admin"] exact component={Dashboard}/>
B) Add a Redirect for admin if you prefer to keep the route as /admin/dashboard
<Redirect exact from="/admin" to={`/admin/dashboard`} />
Edit: (Most importantly)
Also, noticed that you did not include the admin into the main router. You don't need to separate the admin from nav. Suggest to read on the document
https://reactrouter.com/web/api/Switch
Switch renders the first child <Route> or <Redirect> that matches the location.
Overall, it should be combined like this
<Switch>
...user paths
...admin paths
</Switch>
The project I am working on needs better error handling and to begin I've decided to implement reacts ErrorBoundary hook componentDidCatch which I was able to implement simply in a single component. However a senior developer has recommended I make my error boundary a Higher Order Component so that I can wrap the entire application in it. This is where I am running into trouble because despite reading the documentation higher order components make little sense to me.
This is what I have implemented so far:
my HOC
import React from "react";
import ErrorScreen from "./ErrorScreen"
export default function NewErrorHandler(WrappedComponent) {
class ErrorHandler extends React.Component {
constructor(props) {
this.state = {hasError: false}
};
componentDidCatch(error, info) {
this.setState({ hasError: true })
}
render() {
if(this.state.hasError) {
return (
<ErrorScreen/>
)
} else {
return this.props.children
}
}
}
}
My issue so far is Im not exactly sure how to wrap the application in the error boundary. In all the examples Ive seen the HOC's are wrapping around functional components easily through exporting however the way this application is set up there is no explicit exported function that I can see:
import ReactDOM from "react-dom";
import React from "react";
import { store, history } from "./store";
import { Provider } from "react-redux";
import { Route, Switch } from "react-router"; // react-router v4
import { ConnectedRouter } from "connected-react-router";
import DetectAdblock from "./components/DetectAdblock";
import ErrorBound from "./components/ErrorHOC";
const Encounter = Loadable({
loader: () => import("./components/Encounter/Encounter"),
loading: Loading,
delay: 300
});
const VerifyInsuranceList = Loadable({
loader: () => import("./components/VerifyInsurance/InsuranceList"),
loading: Loading,
delay: 300
});
const VideoChat = Loadable({
loader: () => import("./components/VideoChat"),
loading: Loading,
delay: 300
});
document.addEventListener("DOMContentLoaded", () => {
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<TokenLoader>
<DetectAdblock />
<BrowserBanner />
<EnvBanner />
<IdleMonitor />
<TokenRefresher />
<MonotonicClock frequency={15} />
<RtcMonitor />
<PatientPoller pollInterval={30000} />
<Redirect />
<Switch>
<Route exact={true} path="/" component={ProviderDashboard} />
<Route
exact={true}
path="/accept-invitation/:inviteID"
component={AcceptInvite}
/>
<Route exact={true} path="/login" component={Login} />
<Route
exact={true}
path="/reset-password/:resetID"
component={ResetPassword}
/>
<Route
exact={true}
path="/request-password-reset"
component={ForgotPassword}
/>
<Route
exact={true}
path="/waiting-room"
component={ProviderAvailablePatients}
/>
<Route exact={true} path="/encounter" component={Encounter} />
<Route exact={true} path="/video" component={VideoChat} />
<Route
exact={true}
path="/providers"
component={ManagerProviders}
/>
<Route exact={true} path="/providers/new" component={Invite} />
<Route
exact={true}
path="/providers/edit/:providerID"
component={ProviderEdit}
/>
<Route
exact={true}
path="/providers/audit/:providerID"
component={ProviderAudit}
/>
<Route
exact={true}
path="/activity-monitor"
component={ActivitySummary}
/>
<Route
exact={true}
path="/encounter-monitor"
component={EncounterMonitor}
/>
<Route
exact={true}
path="/encounter-monitor/:encounterID"
component={EncounterMonitorDetails}
/>
<Route exact={true} path="/billing" component={BillingTab} />
<Route exact={true} path="/patients" component={PatientTab} />
<Route exact={true} path="/rounding" component={RoundingTab} />
<Route
exact={true}
path="/patients/:patientID"
component={PatientChart}
/>
<Route
exact={true}
path="/active-patient-chart/:patientID"
component={ActivePatientChart}
/>
<Route
exact={true}
path="/insurnace-history/:patientID"
component={InsuranceHistory}
/>
<Route
exact={true}
path="/verify-insurance"
component={VerifyInsuranceList}
/>
<Route render={() => <div>Not Found</div>} />
</Switch>
</TokenLoader>
</ConnectedRouter>
</Provider>
document.getElementById("app")
);
});
Here I have left out some of the import statements but have shown how I am importing my ErrorHOC.js. Any insight into how to wrap the whole application would be super helpful. If I am missing information here needed for understanding please let me know.
As discussed in the comments, error boundary is NOT a use case for HOC, any way here is a possible example of how the logic should work:
// withErrorBoundary.js
class ErrorHandler extends React.Component {}
// HOC wrapping the passed component
export default function withErrorBoundary(WrappedComponent) {
const Component = (
<ErrorHandler>
<WrappedComponent />
</ErrorHandler>
);
return Component;
}
// index.js
import withErrorBoundary from "./withErrorBoundary.js";
const App = <Provider store={store}>...</Provider>;
// Usage
const AppWithErrorBoundary = withErrorBoundary(App);
ReactDOM.render(<AppWithErrorBoundary />, document.getElementById("app"));
Error boundary should be a wrapper component so you can pass helpful props to it, easier reuse case on multiple usages, and more:
class ErrorBoundaryWrapper extends React.Component {
render() {
return (
<>
{/** Use other props passed to wrapper **/}
<div>...</div>
{this.props.children}
</>
);
}
}
// Usage, see the advantage over HOC
<>
<ErrorBoundaryWrapper specialProps={props1}>
<Component1 />
</ErrorBoundaryWrapper>
<ErrorBoundaryWrapper specialProps={props2}>
<Component2 />
</ErrorBoundaryWrapper>
</>
See similar question: What ErrorBoundary actually does.
My Setup:
routes.js
const Router = () => (
<Switch>
<Route path="/" component={ Dashboard } />
<Route path="/somepath" component={ SomePath } />
</Switch>
);
index.js
<HashRouter>
<App />
</HashRouter>
app.js:
lass App extends Component {
render() {
return (
<div className="main-app">
<Header />
<div className="page__container">
<Router />
</div>
<Footer />
</div>
);
}
}
Issue is, when I navigate to localhost/#/ rootpath, it is correctly rendering Dashboard component as mentioned in routes.js file. But When I naviagte to localhost/#/somepath, it not rendering component for somepath, it is stil rendering / Component.
Even in React Devtool it shows <Route path="/"> is loaded, not <Route path="/somepath">
You may have to add exact to match the path. add exact prop to Route
<Route exact path="/somepath" component={ SomePath } />
I seem to be facing a simple issue but would like to know the best way to solve it.
I have the following classical router with RR4:
const AppRouter = () => (
<BrowserRouter>
<div className="content">
<Header />
<Switch>
<Route path="/" component={Index} exact={true} />
<Route path="/admin" component={Admin} exact={true} />
<Route path="/account" component={Account} exact={true} />
<Route path="/login" component={Login} exact={true}/>
<Route path="/signup" component={Signup} exact={true}/>
<Route path="/*" component={NotFound} />
</Switch>
<Footer />
</div>
</BrowserRouter>
);
And I would like to have a case on my Index Root where it does not show the header and footer if the user is not connected but still shows these on any other page (whether connected or not) and on Index when connected. Not sure how to manage this probably simple case. Anyone could help ? thanks in advance !
If the connected state is in something like redux you can just have the header and footer read the state and return null. the header can get hold of the current url props with withRouter
import React, { Component } from 'react';
import { withRouter } from 'react-router';
class HeaderComponent extends Component {
const { match, location, history } = this.props
render() {
if(!connected && location.pathname === '/') return null;
return <div>Header</div>
}
}
const Header = withRouter(HeaderComponent)