Router Router Switch causes unmount on updating route search/query strings

Viewed 25

In my App.js I have a number of components wrapped in a Switch component from react-router-dom

App.js

import React from "react";
import Loadable from "react-loadable";
import { Switch } from "react-router-dom";
import ProtectedRoute from "./ProtectedRoute";

const Test = Loadable({
  loader: () => import("./Test"),
  loading: () => <h1>LOADING....</h1>
});

const Test1 = Loadable({
  loader: () => import("./Test1"),
  loading: () => <h1>LOADING....</h1>
});

const Test2 = Loadable({
  loader: () => import("./Test2"),
  loading: () => <h1>LOADING....</h1>
});

const App = () => {
  return (
    <Switch>
      <ProtectedRoute bgColour="blue" exact path="/" component={Test} />
      <ProtectedRoute bgColour="red" exact path="/1" component={Test1} />
      <ProtectedRoute bgColour="green" exact path="/2" component={Test2} />
    </Switch>
  );
};

export default App;

The ProtectedRoute component renders a Route component from react-router-dom passing in the specified component. It also has a HOC, which in my actual application checks the user is authenticated

ProtectedRoute.js

import React from "react";
import { Route } from "react-router-dom";

const withAuth = (Component) => {
  return (props) => {
    return <Component {...props} />;
  };
};

const ProtectedRoute = ({ component, bgColour, ...args }) => {
  return (
    <div style={{ backgroundColor: bgColour || "transparent" }}>
      <Route component={withAuth(component)} {...args} />
    </div>
  );
};

export default ProtectedRoute;

For each component, I have alerts setup to trigger on mount and unmount of the component. On a click on an element it updates the query string to a random number via history.push, however, this currently triggers an unmount, due to the Switch added in App.js, without the Switch there is no unmount. This is causing an issue in my application as an unmount is not desired behaviour and is causing issues with loading the correct data.

Test.js

import React, { useEffect } from "react";
import { useHistory } from "react-router-dom";

export default function Test() {
  const history = useHistory();
  useEffect(() => {
    alert("MOUNTED BASE");
    return () => {
      alert("UNMOUNTED BASE");
    };
  }, []);
  return (
    <div>
      <h1>TEST COMPONENT BASE - BLUE</h1>
      <div
        onClick={() =>
          history.push({
            pathname: history.location.pathname,
            search: `?query=${Math.random().toFixed(2)}`
          })
        }
      >
        UPDATE QUERY STRING
      </div>
      <div onClick={() => history.push("/1")}>GO TO Next ROUTE</div>
    </div>
  );
}

I still want the functionality of the Switch but prevent the unmount on history.push, is this possible?

I have a CodeSandbox below to recreate this issue

Edit priceless-lucy-w4r13n

1 Answers

Issue

Based on only the code you've provided, an issue I see is how every component the ProtectedRoute renders is decorated with the withAuth Higher Order Component. This results in a new component being created every time ProtectedRoute renders.

Solution

You want to only decorate the routed components with HOCs once prior to where they are used.

Example:

const ProtectedRoute = ({ bgColour, ...props }) => {
  return (
    <div style={{ backgroundColor: bgColour || "transparent" }}>
      <Route {...props} />
    </div>
  );
};

...

import React from "react";
import Loadable from "react-loadable";
import { Switch } from "react-router-dom";
import ProtectedRoute from "./ProtectedRoute";
import withAuth from "..path/to/withAuth";

// Decorate components with HOCs once out here
const Test = withAuth(Loadable({
  loader: () => import("./Test"),
  loading: () => <h1>LOADING....</h1>
}));

const Test1 = withAuth(Loadable({
  loader: () => import("./Test1"),
  loading: () => <h1>LOADING....</h1>
}));

const Test2 = withAuth(Loadable({
  loader: () => import("./Test2"),
  loading: () => <h1>LOADING....</h1>
}));

// Render decorated components in App
const App = () => {
  return (
    <Switch>
      <ProtectedRoute bgColour="red" path="/1" component={Test1} />
      <ProtectedRoute bgColour="green" path="/2" component={Test2} />
      <ProtectedRoute bgColour="blue" path="/" component={Test} />
    </Switch>
  );
};

Edit router-router-switch-causes-unmount-on-updating-route-search-query-strings

Related