I have an App component which houses various routes. One of those routes renders a Dashboard component, like so:
class App extends Component {
render() {
return (
<Router>
<Switch>
<Route exact path = "/" component={Home} />
<Route exact path = "/about" component={AboutPage}/>
<Route exact path = "/business" component={BusinessPage} />
<Route exact path = "/business/dashboard/:business" component={DashBoard} />
<Route exact path = "/business/laundromat/:businessName" component={DefaultPage} />
<Route exact path = "/contact" component={Contact} />
<Route exact path = "/login" component={LoginPage} />
<Route exact path = "/sign-up" component={SignUpPage} />
<Route exact path = "/:category/:zipCode" component={SearchPage} />
<Route path="*" component={NotFound}/>
</Switch>
</Router>
);
}
}
export default App;
This component is stateful and passes data as props to various children components. This works fine. However, when I click on links to these components, the URL does not change. I have decided to use "react-router-dom" and the useRouteMatch hook to create nested components. I believe I have it setup correctly, but when I click on Links to these nested components I get my 404 page which is handled on my App component.
I am simply appending /business-info (etc) to the Path mentioned above within my Dashboard Component. I would like to avoid nesting these routes within my App, but every time I click on one of these links it hits the catch all 404 path. Is it possible to append text after a :param? I only have access to the data I need to pass down within the Dashboard... Here is what my route looks like within my Dashboard Component:
import { useRouteMatch, Link, Route, Switch } from 'react-router-dom';
import BusinessInfo from "../components/DashboardBusinessInfo";
import AccountInfo from "../components/DashboardAccountInfo";
import Services from "../components/DashboardServices";
const {path, url} = useRouteMatch();
<Switch>
<Route path={`${path}/business-info`} element={
<BusinessInfo
name={stateObject.businessName}
phone={stateObject.phone}
address={stateObject.address}
category={stateObject.categoryName}
hours={stateObject.hours}
website={stateObject.website}
zip={stateObject.zip}
city={stateObject.city}
street={stateObject.street}
setLink = {selectLinks}
update={updateCall}
/>}
/>
<Route path={`${path}/account-info`} element={<AccountInfo businessName={stateObject.businessName} setLink = {selectLinks}/>}/>
<Route path={`${path}/services`} element={
<Services
category={linkControl.servicesCategory}
data={stateObject}
update={updateCall}
/>}
/>
</Switch>
Version: [email protected]
CodePudding user response:
You need to remove the exact
from your parent route when you are using nested routes.
<Route path="/business/dashboard/:business" component={DashBoard} />
Exact means 'When true, will only match if the path matches the location.pathname exactly' so all of your nested routes dont match it exactly so it wont go into them.
Check out the reactrouter v5 example for it and you can see that /topics/
isn't using exact.
https://v5.reactrouter.com/web/example/nesting
CodePudding user response:
The issue here is that all your root level routes are exactly matching the URL path. This necessarily precludes them from matching any nested/sub-routes rendered by any matched components. In other words, when the path is "/business/dashboard/someBusinessName/business-info"
the "/business/dashboard/:business"
path no longer exactly matches and the Dashboard
component is unmounted.
To resolve you must remove the exact
prop so the path is treated as a path-prefix and can then match nested/sub-routes. You should also keep in mind that within the Switch
component that path order and specificity matter! Order the route paths from more specific paths to less specific paths so the more specific paths can be attempted to be matched before the less specific ones. If you order the paths correctly there should be a nearly zero use-case for using the exact
prop on the routes.
App
<Router>
<Switch>
<Route path="/about" component={AboutPage}/>
<Route path="/business/dashboard/:business" component={DashBoard} />
<Route path="/business/laundromat/:businessName" component={DefaultPage} />
<Route path="/business" component={BusinessPage} />
<Route path="/contact" component={Contact} />
<Route path="/login" component={LoginPage} />
<Route path="/sign-up" component={SignUpPage} />
<Route path="/:category/:zipCode" component={SearchPage} />
<Route path="/" component={Home} />
<Route path="*" component={NotFound}/>
</Switch>
</Router>
Dashboard
Since you are using react-router-dom
v5 the Route
components don't take an element
prop, this is new prop in v6 that replaced component
, render
, and children
function props. You can just render the components as wrapped children since you are passing along additional props.
<Switch>
<Route path={`${path}/business-info`}>
<BusinessInfo
name={stateObject.businessName}
phone={stateObject.phone}
address={stateObject.address}
category={stateObject.categoryName}
hours={stateObject.hours}
website={stateObject.website}
zip={stateObject.zip}
city={stateObject.city}
street={stateObject.street}
setLink = {selectLinks}
update={updateCall}
/>
</Route>
<Route path={`${path}/account-info`}>
<AccountInfo
businessName={stateObject.businessName}
setLink={selectLinks}
/>
</Route>
<Route path={`${path}/services`}>
<Services
category={linkControl.servicesCategory}
data={stateObject}
update={updateCall}
/>
</Route>
</Switch>