reactjsreact-routercss-transitionsreact-bootstrapreactcsstransitiongroup

React CSSTransition creates new page twice and runs the transtion on these two identical pages


I use react-router and useNavigate to switch between pages. With the CSSTransition I wanted to make a smooth transition between the pages. Unfortunately it doesn't work as intended and the router deletes the current page immediately and creates the new page twice and then executes the transition on these two same pages.

App.js

import "./styles.css";
import "bootstrap/dist/css/bootstrap.min.css";
import {
  BrowserRouter,
  Router,
  Route,
  Routes,
  useLocation
} from "react-router-dom";
import { TransitionGroup, CSSTransition } from "react-transition-group";
import { Nav, Navbar, NavDropdown } from "react-bootstrap";
import { useNavigate } from "react-router-dom";

import Settings from "./Settings";
import TestPage from "./TestPage";

function App() {
  const location = useLocation();
  const navigate = useNavigate();

  return (
    <div className="App">
      <Navbar collapseOnSelect expand="lg" bg="dark" variant="dark">
        <Navbar.Brand href="#home">Blaa</Navbar.Brand>
        <Navbar.Toggle aria-controls="responsive-navbar-nav" />
        <Navbar.Collapse id="responsive-navbar-nav">
          <Nav className="mr-auto">
            <Nav.Link onClick={() => navigate("/settings")}>Settings</Nav.Link>
            <Nav.Link onClick={() => navigate("/testPage")}>TestPage</Nav.Link>
          </Nav>
        </Navbar.Collapse>
      </Navbar>

      <TransitionGroup component={null}>
        <CSSTransition key={location.key} classNames="fade" timeout={3000}>
          <Routes>
            <Route path="/settings" element={<Settings />} />
            <Route path="/testPage" element={<TestPage />} />
          </Routes>
        </CSSTransition>
      </TransitionGroup>
    </div>
  );
}

const Root = () => (
  <BrowserRouter basename={window.location.pathname.replace(/(\/[^/]+)$/, "")}>
    <div className="App">
      <App />
    </div>
    <br />
  </BrowserRouter>
);

export default Root;

Here is the code: https://codesandbox.io/s/cool-moon-l99ogc

What's wrong with this approach? I couldn't find a solution.


Solution

  • Pass the location to the Routes component.

    function App() {
      const location = useLocation();
      const navigate = useNavigate();
    
      return (
        <div className="App">
          <Navbar collapseOnSelect expand="lg" bg="dark" variant="dark">
            <Navbar.Brand href="#home">Blaa</Navbar.Brand>
            <Navbar.Toggle aria-controls="responsive-navbar-nav" />
            <Navbar.Collapse id="responsive-navbar-nav">
              <Nav className="mr-auto">
                <Nav.Link onClick={() => navigate("/settings")}>Settings</Nav.Link>
                <Nav.Link onClick={() => navigate("/testPage")}>TestPage</Nav.Link>
              </Nav>
            </Navbar.Collapse>
          </Navbar>
    
          <TransitionGroup component={null}>
            <CSSTransition key={location.key} classNames="fade" timeout={3000}>
              <Routes
                location={location} // <-- Pass location prop
              >
                <Route path="/settings" element={<Settings />} />
                <Route path="/testPage" element={<TestPage />} />
              </Routes>
            </CSSTransition>
          </TransitionGroup>
        </div>
      );
    }
    

    Edit react-csstransition-creates-new-page-twice-and-runs-the-transtion-on-these-two-i