Reputation: 41
EDIT: I am using React 18.2.0 and React router dom 5.3.3, and I recently replaced React.Strictmode with Fragment this evening in my index.jsx file.
As title says, the application is working well except for the NavBar. When you click the sections it does not render the component onto the page, but the href changes in the browser itself. Here is my NavBar component code:
import { Navbar, Nav, Container } from 'react-bootstrap';
import { withRouter } from 'react-router';
import { NavLink } from 'react-router-dom';
import styled, { ThemeContext } from 'styled-components';
import endpoints from '../constants/endpoints';
import ThemeToggler from './ThemeToggler';
const styles = {
logoStyle: {
width: 50,
height: 40,
},
};
const ExternalNavLink = styled.a`
color: ${(props) => props.theme.navbarTheme.linkColor};
&:hover {
color: ${(props) => props.theme.navbarTheme.linkHoverColor};
}
&::after {
background-color: ${(props) => props.theme.accentColor};
}
`;
const InternalNavLink = styled(NavLink)`
color: ${(props) => props.theme.navbarTheme.linkColor};
&:hover {
color: ${(props) => props.theme.navbarTheme.linkHoverColor};
}
&::after {
background-color: ${(props) => props.theme.accentColor};
}
&.navbar__link--active {
color: ${(props) => props.theme.navbarTheme.linkActiveColor};
}
`;
const NavBar = () => {
const theme = useContext(ThemeContext);
const [data, setData] = useState(null);
const [expanded, setExpanded] = useState(false);
useEffect(() => {
fetch(endpoints.navbar, {
method: 'GET',
})
.then((res) => res.json())
.then((res) => setData(res))
.catch((err) => err);
}, []);
return (
<Navbar
fixed="top"
expand="md"
bg="dark"
variant="dark"
className="navbar-custom"
expanded={expanded}
>
<Container>
{data?.logo && (
<Navbar.Brand href="/">
<img
src={data?.logo?.source}
className="d-inline-block align-top"
alt="main logo"
style={
data?.logo?.height && data?.logo?.width
? { height: data?.logo?.height, width: data?.logo?.width }
: styles.logoStyle
}
/>
</Navbar.Brand>
)}
<Navbar.Toggle
aria-controls="responsive-navbar-nav"
onClick={() => setExpanded(!expanded)}
/>
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="me-auto" />
<Nav>
{data
&& data.sections?.map((section, index) => (section?.type === 'link' ? (
<ExternalNavLink
key={section.title}
href={section.href}
target="_blank"
rel="noopener noreferrer"
onClick={() => setExpanded(false)}
className="navbar__link"
theme={theme}
>
{section.title}
</ExternalNavLink>
) : (
<InternalNavLink
key={section.title}
onClick={() => setExpanded(false)}
exact={index === 0}
activeClassName="navbar__link--active"
className="navbar__link"
to={section.href}
theme={theme}
>
{section.title}
</InternalNavLink>
)))}
</Nav>
<ThemeToggler
onClick={() => setExpanded(false)}
/>
</Navbar.Collapse>
</Container>
</Navbar>
);
};
const NavBarWithRouter = withRouter(NavBar);
export default NavBarWithRouter;
App.jsx:
import React, { useEffect, useState, useContext } from 'react';
import { Navbar, Nav, Container } from 'react-bootstrap';
import { withRouter } from 'react-router';
import { NavLink } from 'react-router-dom';
import styled, { ThemeContext } from 'styled-components';
import endpoints from '../constants/endpoints';
import ThemeToggler from './ThemeToggler';
const styles = {
logoStyle: {
width: 50,
height: 40,
},
};
const ExternalNavLink = styled.a`
color: ${(props) => props.theme.navbarTheme.linkColor};
&:hover {
color: ${(props) => props.theme.navbarTheme.linkHoverColor};
}
&::after {
background-color: ${(props) => props.theme.accentColor};
}
`;
const InternalNavLink = styled(NavLink)`
color: ${(props) => props.theme.navbarTheme.linkColor};
&:hover {
color: ${(props) => props.theme.navbarTheme.linkHoverColor};
}
&::after {
background-color: ${(props) => props.theme.accentColor};
}
&.navbar__link--active {
color: ${(props) => props.theme.navbarTheme.linkActiveColor};
}
`;
const NavBar = () => {
const theme = useContext(ThemeContext);
const [data, setData] = useState(null);
const [expanded, setExpanded] = useState(false);
useEffect(() => {
fetch(endpoints.navbar, {
method: 'GET',
})
.then((res) => res.json())
.then((res) => setData(res))
.catch((err) => err);
}, []);
return (
<Navbar
fixed="top"
expand="md"
bg="dark"
variant="dark"
className="navbar-custom"
expanded={expanded}
>
<Container>
{data?.logo && (
<Navbar.Brand href="/">
<img
src={data?.logo?.source}
className="d-inline-block align-top"
alt="main logo"
style={
data?.logo?.height && data?.logo?.width
? { height: data?.logo?.height, width: data?.logo?.width }
: styles.logoStyle
}
/>
</Navbar.Brand>
)}
<Navbar.Toggle
aria-controls="responsive-navbar-nav"
onClick={() => setExpanded(!expanded)}
/>
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="me-auto" />
<Nav>
{data
&& data.sections?.map((section, index) => (section?.type === 'link' ? (
<ExternalNavLink
key={section.title}
href={section.href}
target="_blank"
rel="noopener noreferrer"
onClick={() => setExpanded(false)}
className="navbar__link"
theme={theme}
>
{section.title}
</ExternalNavLink>
) : (
<InternalNavLink
key={section.title}
onClick={() => setExpanded(false)}
exact={index === 0}
activeClassName="navbar__link--active"
className="navbar__link"
to={section.href}
theme={theme}
>
{section.title}
</InternalNavLink>
)))}
</Nav>
<ThemeToggler
onClick={() => setExpanded(false)}
/>
</Navbar.Collapse>
</Container>
</Navbar>
);
};
const NavBarWithRouter = withRouter
(NavBar);
export default NavBarWithRouter;
MainApp.jsx:
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import FallbackSpinner from './components/FallbackSpinner';
import NavBarWithRouter from './components/NavBar';
import Home from './components/Home';
import endpoints from './constants/endpoints';
function MainApp() {
const [data, setData] = useState(null);
useEffect(() => {
fetch(endpoints.routes, {
method: 'GET',
})
.then((res) => res.json())
.then((res) => setData(res))
.catch((err) => err);
}, []);
return (
<div className="MainApp">
<NavBarWithRouter />
<Router className="main">
<Switch>
<Suspense fallback={<FallbackSpinner />}>
<Route exact path="/" component={Home} />
{data
&& data.sections.map((route) => {
const SectionComponent = React.lazy(() => import('./components/' + route.component));
return (
<Route
key={route.headerTitle}
path={route.path}
component={() => (
<SectionComponent header={route.headerTitle} />
)}
/>
);
})}
</Suspense>
</Switch>
</Router>
</div>
);
}
export default MainApp;
I appreciate any and all help on this, I've been pulling my hair out over it!
Upvotes: 1
Views: 562
Reputation: 202608
Actually, besides recommending upgrading to [email protected]
or better, the other issue I see is the NavBar
being rendered outside the Router
. Move it into the Router
and remove the withRouter
HOC. The withRouter
HOC only injects the route props of the routing context above it in the ReactTree and there is none.
NavBar
Instead of
const NavBarWithRouter = withRouter(NavBar);
export default NavBarWithRouter;
use
export default NavBar;
...
import NavBar from './components/NavBar';
...
function MainApp() {
...
return (
<div className="MainApp">
<Router className="main">
<NavBar /> // <-- inside router
<Switch>
<Suspense fallback={<FallbackSpinner />}>
<Route exact path="/" component={Home} />
{data
&& data.sections.map((route) => {
const SectionComponent = React.lazy(() => import('./components/' + route.component));
return (
<Route
key={route.headerTitle}
path={route.path}
component={() => (
<SectionComponent header={route.headerTitle} />
)}
/>
);
})}
</Suspense>
</Switch>
</Router>
</div>
);
}
Upvotes: 1