skip to Main Content

The first time the page is loaded, the value of renderCount will be 1. However, when I start typing in the input, the value of renderCount jumps directly from 1 to 3.

I checked in Chrome under Components, and there useRef appears with the value 3 right from the page mount, even though I set it to 1. I apologize if my question is not very relevant, but I just started learning React and useRef, and I was watching a WebDevSimplified video explaining how useRef works. I wrote the code exactly like him, but the behavior is different for me. The video is from 4 years ago, and it’s an older version of React, so that might be the issue. Could you please explain what I should do so that useRef starts at 1 as I set it, and when I type in the input for the first time, it goes to the value 2 instead of 3?

I thought it might be some behavior from useEffect, so I tried setting the dependency to renderCount.current, but the behavior is the same.

function App() {
  const [name, setName] = useState("");
  const renderCount = useRef(1);

  useEffect(() => {
    renderCount.current = renderCount.current + 1;
  }, [renderCount]);

  return (
    <>
      <input value={name} onChange={(e) => setName(e.target.value)}></input>
      <div>My name is {name}</div>
      <div>I rendered {renderCount.current} times </div>
    </>
  );
}

2

Answers


  1. Strict Mode Docs: https://react.dev/reference/react/StrictMode

    Remove strict mode.

    With strict mode, the 1st time the useEffect is called, it will be called a 2nd time

    import { createRoot } from "react-dom/client";
    import App from "./App.tsx";
    import "./index.css";
    
    // Find this code in your app.
    
    // createRoot(document.getElementById('root')!).render(
    //   <StrictMode>
    //     <App />
    //   </StrictMode>,
    // )
    
    // Change it to this:
    
    createRoot(document.getElementById("root")!).render(<App />);
    

    Then remove the dependency array to run the effect on every render.

    function App() {
      const [name, setName] = useState("");
      const renderCount = useRef(1);
    
      useEffect(() => {
        renderCount.current = renderCount.current + 1;
      });
    
      return (
        <>
          <input value={name} onChange={(e) => setName(e.target.value)}></input>
          <div>My name is {name}</div>
          <div>I rendered {renderCount.current} times </div>
        </>
      );
    }
    

    Your counter should now start at 1. And increase by one on every keystroke.

    Login or Signup to reply.
  2. It may the right time for you to get in touch with the developmental aid provided by React team. It is the Strict Mode which is coded in index.js as below.

    index.js

      <StrictMode>
        <App />
      </StrictMode>
    

    In a nutshell, it is a developmental aid , therefore it is useful to become familiar with it and take its advantage.

    In development mode, every react component React mounts twice. It happens in this way – mount, unmount and then mount again. The notable point over here is the unmount event which is an ideal place to run clean up codes if any. The importance of this event comes when there are some heavy resources loaded during the mount event and it is needed to be released on each unmount.

    Let us say there is database connection opened during the mount event of a component. And when the user navigates to the other parts of an App, the first component may be unmounted by the App. However the database connection initiated earlier will remain active. When the user navigates back to the same component again, it would mount again and open another database connection which is in addition to the first connection. Such mistakes are difficult to spot.

    The Strict mode comes handy in such cases. In Strict Mode, the component will be loaded twice. However, Strict Mode alone is useless. Its power comes out when used with the complementing development best practices. For example, logging function calls for debugging.

    Let us see a sample code below.

    The button lets the user to navigate between the two components.

    App.js

    import { useEffect, useState } from 'react';
    
    export default function App() {
      const [navigate, setNavigate] = useState(false);
      return (
        <>
          {!navigate && <ComponentOne />}
          {navigate && <ComponentTwo />}
          <br />
          <button onClick={() => setNavigate(!navigate)}>
            Navigate to and fro
          </button>
        </>
      );
    }
    
    function ComponentOne() {
      useEffect(() => {
        console.log('ComponentOne: Some database connected established');
        return () =>
          console.log('ComponentOne: The database connection has been released');
      }, []);
      return 'I am the component One';
    }
    
    function ComponentTwo() {
      return 'I am the component Two';
    }
    

    Test run – console logs

    Let us analyse the logs it generated. The below logs are the results of loading the app and then clicking the button one time. It means, the ComponentOne has loaded in the initial render. And then the user clicked on the button to navigate to ComponentTwo.

    The logs clearly show that the database connection has been evenly handled, it has established and released on every mount and unmount event of ComponentOne. There is no gap in the code leading to orphaned connections.

    // ComponentOne: Some database connected established
    // ComponentOne: The database connection has been released
    // ComponentOne: Some database connected established
    // ComponentOne: The database connection has been released
    

    Now please have a close look on the action by Strict Mode. Please inspect the logs below on load of the App, that is before making the first click. You may ask yourself, why do we get three entries here ? It is because React has loaded ComponentOne Twice – mount, unmount and then mount. By this, React helps the developers to spot issues if any from the logs and ensure the codes are provided evenly.

    // ComponentOne: Some database connected established
    // ComponentOne: The database connection has been released
    // ComponentOne: Some database connected established
    

    Let us take the case that the clean up code has not been implemented by the developer by mistake. Then the logs would have been even as below. By seeing two such lines without the counterpart in between, it points out the mistake in the coding. This is the work of Strict Mode as a developmental aid.

    // ComponentOne: Some database connected established
    // ComponentOne: Some database connected established
    

    Now when it comes to your question, although there is no costly database connections like the examples discussed above, the side effect made in useEffect may be undone by using a clean up function as below.

    App.js

    import { useEffect, useState } from 'react';
    
    function App() {
      const [name, setName] = useState('');
      const renderCount = useRef(1);
    
      useEffect(() => {
        renderCount.current = renderCount.current + 1;
        return () => (renderCount.current = renderCount.current - 1);
      });
    
      return (
        <>
          <input value={name} onChange={(e) => setName(e.target.value)}></input>
          <div>My name is {name}</div>
          <div>I rendered {renderCount.current} times </div>
        </>
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search