2

I want to understand when React mounts and unmounts a component. The official document on useEffect seems to suggest that cleanup action always run so that we can rely on it to unsubscribe to some API. However, I find that it isn't always the case. In the following toy example, I define clean-up actions for both <About> and <About2>, I expect both would unmount first before they are mounted again. As a result, the "mounting" messages should always come after an "unmounting" message (except for the first "mounting" message). This indeed is the case if I switch between these two pages by clicking the <Link>s. However, if I switch between these two pages by directly editing the browser's url bar, there is no "unmounting" message. That is, the clean-up actions as defined in useEffect don't run at all in this case. I suspect the reason is being 'page reloading'(by which, I mean react somehow abandons whatever it has and reconstruct everything from scratch). My questions are:

  1. Does editing browser's url directly always trigger a 'page reloading'?
  2. How can we make sure the clean-up actions(eg. unsubscribe to an API) still run in case of 'page reloading'. As shown by this example, clean-up action defined in useEffect may not always work.

Thank you!

import React, { useEffect } from 'react';
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";


function App() {
  return (
    <BrowserRouter>
        <div>
          <ul>
            <li>
              <Link to="About"> About</Link>
            </li>
            <li>
              <Link to="About2"> About2</Link>
            </li>
            
          </ul>

          <hr />

          <Routes>

            <Route path="/about" element={<About />} />
            <Route path="/about2" element={<About2 />} />
          </Routes>
        </div>
    </BrowserRouter>
  );
}

function About() {
  useEffect(()=>{
    console.log("Mounting About");
    return ()=>{console.log("Unmounting About");};
  },[]);
  console.log("In About");
  return (
    <div>About</div>
  );
}

function About2() {
  useEffect(()=>{
    console.log("Mounting About2");
    return ()=>{console.log("Unmounting About2");};
  },[]);
  console.log("In About2");
  return (
    <div>About2</div>
  );
}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
sgu
  • 1,301
  • 1
  • 13
  • 25

1 Answers1

2

If you directly edit the URL, there's no clean up needed to be done since it is pretty much the same as closing the tab and destroying the process (the memory usage will be cleared too -- although browsers might leak, for example there is/was a bug in Chrome not cleaning up Web Audio API memory usage properly).

You can test this by adding this (annoying code) on the browser console on any tab:

window.onbeforeunload = function () {
    return "Do you really want to close?";
};

The exception to this rule is when the #hash part is edited:

http://hi.com/#there -> http://hi.com/#hello

No reload is triggered, only the hashchange event. The original idea of the fragment identifier was quite different from its use now.

After editing the url bar, you will see the unload message, which would be the same if you were trying to close the tab/window.

NOTE: I am not qualified to tell you if there is any security-related issue or vulnerability (such as <a> tag's rel="noreferrer"), but I don't believe it might is the case.

Yuan-Hao Chiang
  • 2,484
  • 9
  • 20