When i hit locathost:3000/faq or any other valid routes React is displaying both valid routes content and NotFound(basically 404 custom page) component at the bottom of page. I want to display NotFound component content only if I hit invalid route Ex: localhost:3000/not-existing-url
const routes = [
{
path: '/',
component: HomePage,
is_private: false,
},
{
path: '/faq',
component: RedirectFaqToSupport,
is_private: false,
},
{
path: '/about-us',
component: About,
is_private: false,
},
{
path: '/contact-us',
component: Contact,
is_private: false,
},
{
component: NotFound,
is_private: false,
path: '*'
},
]
Here is my App.js component code.
class App extends Component {
constructor() {
super()
this.state = {
redirectToReferrer: false, // may not need this anymore
}
}
render() {
const { isMobile } = this.props
const renderLoader = () => (
<Columns.Column size={12}>
<div className="has-text-centered" style={{ marginTop: '15%' }}>
<i className="fas fa-3x fa-cog fa-spin"></i>
</div>
</Columns.Column>
)
return (
<Suspense fallback={renderLoader()}>
<BrowserRouter>
<Provider>
<ScrollToTop />
<Subscribe to={[AppContainer]}>
{(auth) => {
return (
<>
<Header
isAuth={auth.state.isAuth}
modalViewType={auth.state.modalViewType}
unreadMessageCount={auth.state.unreadMessageCount}
auth={auth}
/>
<ErrorBoundary>
<div className="page-master-container">
<Switch>
<BuildRoutes
isMobile={isMobile}
isAuth={auth.state.isAuth}
auth={auth}
/>
</Switch>
</div>
</ErrorBoundary>
<Footer isAuth={auth.state.isAuth} />
</>
)
}}
</Subscribe>
</Provider>
</BrowserRouter>
</Suspense>
)
}
}
Here is BuidRoutes component code
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={(props) =>
rest.isAuth === true ? (
<Component {...props} {...rest} />
) : (
<Redirect
to={{
pathname: '/login',
state: { from: props.location },
}}
/>
)
}
/>
)
export const BuildRoutes = ({ isMobile, isAuth, auth }) => {
const location = useLocation()
useEffect(() => {
const queryParams = QueryString.parse(location.search)
setFeatureFlag(queryParams)
}, [location])
return (
<>
{routes.map(
({ path, component: Component, is_private, is_mother }, key) =>
!is_private ? (
<Route
exact={is_mother ? false : true}
path={path}
key={key}
render={(props) => (
<Component
{...props}
isMobile={isMobile}
isAuth={isAuth}
auth={auth}
/>
)}
/>
) : (
<PrivateRoute
isAuth={isAuth}
isMobile={isMobile}
auth={auth}
exact={is_mother ? false : true}
path={path}
key={key}
component={Component}
/>
)
)}
<Route
exact
path="/brand/:brandName"
render={(props) => {
const { brandName } = props.match.params
if (bikeBrands.includes(brandName)) {
return <Redirect to={`/brand/${brandName}-rental`} />
}
return <BrandLandingPage brandName={brandName} />
}}
/>
</>
)
}
Note: Also I tried below method but it didn't help. Still displays NotFound component along with valid routes component
export const BuildRoutes = ({ isMobile, isAuth, auth }) => {
const location = useLocation()
useEffect(() => {
const queryParams = QueryString.parse(location.search)
setFeatureFlag(queryParams)
}, [location])
return (
<>
{routes.map(
({ path, component: Component, is_private, is_mother }, key) =>
!is_private ? (
<Route
exact={is_mother ? false : true}
path={path}
key={key}
render={(props) => (
<Component
{...props}
isMobile={isMobile}
isAuth={isAuth}
auth={auth}
/>
)}
/>
) : (
<PrivateRoute
isAuth={isAuth}
isMobile={isMobile}
auth={auth}
exact={is_mother ? false : true}
path={path}
key={key}
component={Component}
/>
)
)}
<Route
component={NotFound}
/>
<Route
exact
path="/brand/:brandName"
render={(props) => {
const { brandName } = props.match.params
if (bikeBrands.includes(brandName)) {
return <Redirect to={`/brand/${brandName}-rental`} />
}
return <BrandLandingPage brandName={brandName} />
}}
/>
</>
)
}
I am using "react-router-dom": "^5.2.0",
The Switch
component renders the first child <Route>
or <Redirect>
that matches the location. This has the unfortunate result of also rendering the first non-Route
and non-Redirect
child it comes across, BuildRoutes
in this case.
<Switch>
<BuildRoutes // <-- first child/match and always rendered
isMobile={isMobile}
isAuth={auth.state.isAuth}
auth={auth}
/>
</Switch>
Key points to keep in mind:
Switch
component are exclusively matched and rendered, e.g. the "first matching child...".The BuildRoutes
component is inclusively rendering the routes it renders since the routes are not directly children of a Switch
component. This means that the NotFound
component rendered on path="*"
will always match and always get rendered.
Move the Switch
into BuildRoutes
so that it directly renders the routes as children and so can apply exclusive route matching and rendering.
<ErrorBoundary>
<div className="page-master-container">
<BuildRoutes
isMobile={isMobile}
isAuth={auth.state.isAuth}
auth={auth}
/>
/div>
</ErrorBoundary>
const PrivateRoute = ({ isAuth, ...props }) => {
const from = useLocation();
return isAuth
? <Route {...props} />
: <Redirect to={{ pathname: '/login', state: { from } }} />;
};
export const BuildRoutes = ({ isMobile, isAuth, auth }) => {
const location = useLocation();
useEffect(() => {
const queryParams = QueryString.parse(location.search);
setFeatureFlag(queryParams);
}, [location]);
return (
<Switch>
{routes.map(({ path, component, is_private, is_mother }) => {
const ChildRoute = is_private ? PrivateRoute : Route;
const Component = component;
return (
<ChildRoute
key={path}
exact={!is_mother}
path={path}
render={(props) => (
<Component {...props} {...{ isMobile, isAuth, auth }} />
)}
/>
);
})}
<Route
exact
path="/brand/:brandName"
render={(props) => {
const { brandName } = props.match.params
if (bikeBrands.includes(brandName)) {
return <Redirect to={`/brand/${brandName}-rental`} />
}
return <BrandLandingPage brandName={brandName} />
}}
/>
</Switch>
);
};