0

I have a simple web page with login and search pages. I also have a navbar at the top to allow for switching between the two. The basic App.js looks as follows:

const history = createBrowserHistory();

function App() {
    return (
        <Router history={history}>
            <CustomLayout>
                <Switch>
                    <BaseRouter/>
                </Switch>
            </CustomLayout>
        </Router>
    );
}

export default App;

Now, the BaseRouter and CustomLayout are just

const BaseRouter = () => (
    <div>
        <Route exact path={"/list"} component={ItemsList}/>
        <Route path={"/login"} component={LoginForm}/>
    </div>
);

export default BaseRouter;

and

const CustomLayout = ({children}) => {
    return(
        <>
        <Navbar/>
        {children}
        </>
    );
}

export default CustomLayout;

Now, the navbar looks like this

import React from "react";
import {Menu} from 'antd';
import {Link} from "react-router-dom";

 const Navbar = () => {
    return (
        <div>
            <Menu mode="horizontal" theme={"dark"}>
                <Menu.Item key="list">
                    <Link to={"/list"}>List</Link>
                </Menu.Item>
                <Menu.Item key={"login"}>
                    <Link to={"/login"}>Sign in</Link>
                </Menu.Item>
            </Menu>
        </div>
    );
}

export default Navbar

Let's keep components simple:

const Login = () => {
    return (
        <div>
            login
        </div>
    );
}

export default Login

const List = () => {
    return (
        <div>
            list
        </div>
    );
}

export default List

Now the problem is that when I click on links in the navbar, React doesn't re-render components even though the route changes. I've seen hundreds of answers on SO but I still can't figure it out.
NOTE
It is important for me to avoid refreshing or reloading the page.
EDIT
Strangely enough, when I change Router to BrowserRotuer it works fine, but I can't use my own history then.

Colonder
  • 1,336
  • 1
  • 16
  • 36

4 Answers4

1

why don't you use BrowserRouter from react-router-dom package.

  • App.js:- use BrowserRouter from react-router-dom
import { BrowserRouter as Router, Switch } from 'react-router-dom'

function App() {
  return (
    <Router>
      <CustomLayout>
        <Switch>
          <BaseRouter/>
        </Switch>
      </CustomLayout>
    </Router>
  );
}

export default App;
  • BaseRouter.js:- import Route from react-router-dom
import { Route } from 'react-router-dom'

const BaseRouter = () => (
  <div>
    <Route exact path="/list" component={ItemsList}/>
    <Route path="/login" component={LoginForm}/>
  </div>
);

export default BaseRouter;
  • Navbar.js:-
import React from "react";
import {Menu} from 'antd';
import {Link} from "react-router-dom";

const Navbar = () => {
  return (
    <div>
      <Menu mode="horizontal" theme={"dark"}>
        <Menu.Item key={"list"}>
          <Link to="/list">List</Link>
        </Menu.Item>
        <Menu.Item key={"login"}>
          <Link to="/login">Sign in</Link>
        </Menu.Item>
      </Menu>
    </div>
  );
}

export default Navbar

then if you want to use history:-

import { useHistory } from 'react-router-dom'

const testFunctionComponent = () => {
  const history = useHistory()
  
  const handleClick = (urlPath) => {
    // you can do
    history.push(urlPath) // to go anywhere 
  }

  return (
    <>
      <button onClick={() => handleClick('/anypath')}>Click Me!<button>
    </>
  )
}
lala
  • 617
  • 3
  • 9
  • To whomever that reads this: this is the easiest way to go. I also found that if you use `BrowserRouter` you can call `props.history.push()` in whichever component you like. – Colonder Nov 13 '20 at 14:25
  • Yes. That's true for all components being the children under the `BrowserRouter`. – lala Nov 13 '20 at 14:57
0

Change your BaseRouter from this :

const BaseRouter = () => (
    <div>
        <Route exact path={"/list"} component={ItemsList}/>
        <Route path={"/login"} component={LoginForm}/>
    </div>
);

export default BaseRouter;

To this :

const BaseRouter = () => (
    <div>
        <Route exact path="/list" component={ItemsList}/>
        <Route path="/login" component={LoginForm}/>
    </div>
);

export default BaseRouter;
Gayatri Dipali
  • 667
  • 1
  • 12
0

I believe you cannot have the div inside the switch. You're not exposing the Route components to your switch statement.

Therefore, your url changes because your Navbar makes it change but your switch doesn't know what to do.

Try changing your base router to this:

const history = createBrowserHistory();

function App() {
    return (
        <Router history={history}>
            <CustomLayout>

                    <BaseRouter/>

            </CustomLayout>
        </Router>
    );
}

export default App;
const BaseRouter = () => (
    <Switch>
        <Route exact path={"/list"} component={ItemsList}/>
        <Route path={"/login"} component={LoginForm}/>
    </Switch>
);

export default BaseRouter;
Thales Kenne
  • 1,765
  • 6
  • 20
0

Router is low-level and expects you to manage things. BrowserRouter already syncs with HTML5 history. If you want Router, I think you have to manage the syncing yourself (e.g. <Link onClick={() => history.push(href)}>) or listen to history for change detection. See Detect Route Change with react-router

Philip Ramirez
  • 2,822
  • 1
  • 13
  • 16