1

The thing I'm trying is a multipage react app where I can navigate between pages(like from the main route '/' to '/whitepape' or 'privacyPolicy'), but I have 4 different routes('/', 'services', 'features', 'contactUs') within the main route '/' which use react-scroll to get the scrolling between those 4 components whose links are added in the Navbar(this part works as expected).

But navigating between pages like replacing entirely all 4 components with whitepaper page or privacyPolicy page, with Navbar and Footer at the same place so that I can navigate back to home.

This is what I’m stuck at any help is appreciated I’m not sure I’m following the right way to implement what I need. I have been using React Navigation in react native which is simple to understand and straightforward, wish react-router was so straight forward instead react-router is a bit confusing.

App.js

function App() {
  return (
    <div className="App">
      <Navbar />
      <Switch>
        <Route path="/">
          <>
            <Hero />
            <Services />
            <Features />
            <ContactUs />
          </>
        </Route>
        <Route path="/whitepaper">
          <WhitePaper />
        </Route>
      </Switch>
      <Footer />
    </div>
  )
}

Navbar.js

import { Link } from 'react-scroll'
import { withRouter, Link as RouterLink } from 'react-router-dom'

const menuItems = [
  {
    menuTitle: 'Home',
    pageURL: '/',
    id: 'hero'
  },
  {
    menuTitle: 'Services',
    pageURL: '/services',
    id: 'services'
  },
  {
    menuTitle: 'Features',
    pageURL: '/features',
    id: 'features'
  },
  {
    menuTitle: 'Contact Us',
    pageURL: '/contactUs',
    id: 'contactUs'
  }
]

const Navbar = ({ history }) => {
  const classes = useStyles()
  const [anchorEl, setAnchorEl] = useState(null)
  const [showNav, setShowNav] = useState(false)
  const open = Boolean(anchorEl)
  const theme = useTheme()
  const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
  const [isActive, setIsActive] = useState('/')

  useEffect(() => {
    setIsActive(history.location.pathname)
    window.addEventListener('scroll', () => {
      if (window.scrollY > 100) {
        setShowNav(true)
      } else {
        setShowNav(false)
      }
    })
    return () => {
      window.removeEventListener('scroll', () => setShowNav(false))
    }
  })

  const handleMenu = event => {
    setAnchorEl(event.currentTarget)
  }

  const handleMenuClick = pageURL => {
    history.push(pageURL)
    setAnchorEl(null)
  }

  const handleClose = () => {
    setAnchorEl(null)
  }

  const handleLinkClick = pageURL => {
    history.push(pageURL)
  }

  return (
    <div>
      <AppBar
        position="static"
        elevation={0}
        className={`${classes.appBar} ${
          showNav ? classes.appBarOnScroll : classes.appBar
        }`}>
        <Container>
          <Toolbar className={classes.toolbar}>
            <Link to="hero" smooth duration={1000}>
              <RouterLink to="/">
                <img src={logo} alt="Company Logo" className={classes.logo} />
              </RouterLink>
            </Link>
            {isMobile ? (
              <div>
                <IconButton
                  edge="start"
                  className={classes.menuButton}
                  color="inherit"
                  onClick={handleMenu}
                  aria-label="menu">
                  <MenuIcon />
                </IconButton>
                <Menu
                  className={classes.mobileMenu}
                  id="menu-appbar"
                  anchorEl={anchorEl}
                  anchorOrigin={{
                    vertical: 'top',
                    horizontal: 'right'
                  }}
                  keepMounted
                  transformOrigin={{
                    vertical: 'top',
                    horizontal: 'right'
                  }}
                  open={open}
                  onClose={handleClose}>
                  {menuItems.map(menuItem => {
                    const { menuTitle, pageURL, id } = menuItem
                    return (
                      <Link key={menuTitle} to={id} smooth duration={1000}>
                        <MenuItem
                          className={
                            isActive === pageURL
                              ? classes.mobileActiveMenuItem
                              : classes.mobileMenuItem
                          }
                          onClick={() => handleMenuClick(pageURL)}>
                          {menuTitle}
                        </MenuItem>
                      </Link>
                    )
                  })}
                </Menu>
              </div>
            ) : (
              <div className={classes.menu}>
                <Link
                  to="home"
                  smooth
                  duration={1000}
                  className={
                    isActive === '/' ? classes.menuActiveLink : classes.menuLink
                  }
                  variant="contained"
                  onClick={() => handleLinkClick('/')}>
                  Home
                </Link>
                <Link
                  to="services"
                  smooth
                  duration={1000}
                  className={
                    isActive === '/services'
                      ? classes.menuActiveLink
                      : classes.menuLink
                  }
                  variant="contained"
                  onClick={() => handleLinkClick('/services')}>
                  Services
                </Link>
                <Link
                  to="features"
                  smooth
                  duration={1000}
                  className={
                    isActive === '/features'
                      ? classes.menuActiveLink
                      : classes.menuLink
                  }
                  variant="contained"
                  onClick={() => handleLinkClick('/features')}>
                  Features
                </Link>
                <Link
                  to="contactUs"
                  smooth
                  duration={1000}
                  className={
                    isActive === '/contactUs'
                      ? classes.menuActiveLink
                      : classes.menuLink
                  }
                  variant="contained"
                  onClick={() => handleLinkClick('/contactUs')}>
                  ContactUs
                </Link>
              </div>
            )}
          </Toolbar>
        </Container>
      </AppBar>
    </div>
  )
}

export default withRouter(Navbar)

Footer.js

import { Link } from 'react-scroll'
import { Link as RouterLink } from 'react-router-dom'

const Footer = () => {
  const classes = useStyles()
  const icons = [
    {
      img: linkedin,
      alt: 'linkedin',
      url: 'https://www.linkedin.com/company/company'
    },
    {
      img: twitter,
      alt: 'twitter',
      url: 'https://twitter.com/company'
    },
    {
      img: instagram,
      alt: 'instagram',
      url: 'https://www.instagram.com/company/'
    },
    {
      img: facebook,
      alt: 'facebook',
      url: 'https://www.facebook.com/company/'
    }
  ]
  return (
    <div className={classes.footer}>
      <Container className={classes.container}>
        <Grid container spacing={4}>
          <Grid item md={3} xs={12}>
            <div className={classes.column}>
              <Link to="hero" smooth duration={1000}>
                <RouterLink to="/">
                  <img className={classes.logo} src={logo} alt="Company Logo" />
                </RouterLink>
              </Link>
            </div>
          </Grid>
          <Grid item md={3} xs={12}>
            <div className={classes.column}>
              <div>
                <Grid item>
                  <span className={classes.linksTitle}>USEFUL LINKS</span>
                </Grid>
                <Grid item className={classes.links}>
                  <RouterLink to="/whitepaper" replace className={classes.link}>
                    <span className={classes.span}>White Paper</span>
                  </RouterLink>
                </Grid>
                <Grid item className={classes.links}>
                  <a
                    href="mailto:[email protected]"
                    className={classes.link}>
                    <span className={classes.span}>Email Our CEO</span>
                  </a>
                </Grid>
                <Grid item className={classes.links}>
                  <RouterLink to="/" className={classes.link}>
                    <span className={classes.span}>Privacy Policy</span>
                  </RouterLink>
                </Grid>
              </div>
            </div>
          </Grid>
          <Grid item md={3} xs={12}>
            <div className={classes.column}>
              {icons.map(icon => {
                return (
                  <a href={icon.url} key={icon.alt}>
                    <img
                      className={classes.icon}
                      src={icon.img}
                      alt={icon.alt}
                    />
                  </a>
                )
              })}
            </div>
          </Grid>
          <Grid item md={3} xs={12}>
            <div className={classes.top}>
              <Link to="hero" smooth duration={1000}>
                <RouterLink to="/">
                  <div>
                    <img src={top} alt="back to top" />
                    <p>Back To Top</p>
                  </div>
                </RouterLink>
              </Link>
            </div>
          </Grid>
        </Grid>
        <Grid container>
          <Grid item xs={12}>
            <div className={classes.footerBottom}>
              <p className={classes.copyright}>
                © 2021 Copyrights •{' '}
                <Link to="hero" smooth duration={1000}>
                  <RouterLink to="/" className={classes.company}>
                    <span>company.com</span>
                  </RouterLink>
                </Link>
              </p>
            </div>
          </Grid>
        </Grid>
      </Container>
    </div>
  )
}

export default Footer
2
  • Interesting. I find react-router to be much more straight forward than react-navigation in RN, /shrug. What exactly is the issue here? Is it that your path="/whitepaper" isn't reachable/rendering? Are you rendering a Router around/above App in the ReactTree so there's a routing context? (I suspect there is since you aren't complaining about errors related to that) Commented May 20, 2021 at 5:28
  • @DrewReese I have 4 components(Hero, Services, Features, and ContactUs) all these are rendered in sequence, whose links are in the Navbar I wanted a scrolling when these links in the navbar are clicked to scroll to that component which I was able to achieve with react-scroll Link which works as expected but now I want to replace all these 4 components with a new page i.e. either Whitepaper or PrivacyPolicy page whose link are added in the Footer. I have used Link from react-router-dom as RouterLink iIsee the change in URL but the whitepaper page is not rendered. Commented May 20, 2021 at 5:37

1 Answer 1

1

Within the Switch component path order and specificity matter. This isn't a detail that is overtly called out in their docs though. You want to order your more specific paths before less specific paths. Think of path as more of a prefix, and you'll see that "/" is a path prefix for all paths.

The Switch returns and renders the first matching path it finds in its children.

Just invert the order of your paths such that "/whitepaper" is listed prior to the more general/less specific "/" path.

function App() {
  return (
    <div className="App">
      <Navbar />
      <Switch>
        <Route path="/whitepaper">
          <WhitePaper />
        </Route>
        <Route path="/">
          <>
            <Hero />
            <Services />
            <Features />
            <ContactUs />
          </>
        </Route>
      </Switch>
      <Footer />
    </div>
  )
}
Sign up to request clarification or add additional context in comments.

7 Comments

Now that I can navigate from whitepaper page back to hero/services/features/contactUs using Navbar but when I click on either of those links in Navbar It takes me to the hero instead of that specific component say like contactUs component from whitepaper.
And also i get this warning Warning: validateDOMNesting(...): <a> cannot appear as a descendant of <a> maybe that's because I have nested Link from react-router-dom in Link from react-scroll. Any idea how I can resolve it? And is the most efficient way I can achieve it or are there any good practices for that?
@saicharanpogul react-router-dom out-of-the-box really only handles navigating to pages, but not to a specific part of a page, but luckily there is react-router-hash-link where you can link to anchor tags within a page. About the <a> cannot appear as a descendant of <a> issue... yeah, you can't nest them. I suspect the react-router-dom Link components aren't doing much for you since you aren't technically rendering your hero components on individual routes. You can probably remove the react-router-dom links.
Yes, that makes sense I removed react-router-dom Link. Not sure using react-router-hash-link HashLink and 'react-scroll` Link will turn out as nesting causes warnings.
@SaicharanPogul I've not ever heard of react-scroll (well, maybe I have and just never looked into it), but I would think internal to the hero component you could continue using the scrolling links that you say work, and external you can link to specific anchor/hashlinks on the page and they should be scrolled to. This being said, I have no idea if these two libraries have any conflicting behaviors. Does the internal/external part make sense though?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.