Monday, 3 February 2025

Understanding React Component Remounting: How Changing the key Prop Triggers Remounting?

 

In React, when the key prop of a component changes, React will remount that component rather than just re-render it.

 

Let's get a quick glance on the mounting, rendering, re-rendering, and remounting to clarify how components are created, updated, and reinitialized.

 

1. Mounting

Mounting is the process when a component is created and added to the DOM for the first time. This happens the very first time a component is rendered in the application.

 

During mounting:

 

·      The component’s initial state is set (if using useState or this.state in class components).

·      Lifecycle methods in class components, like componentDidMount, are called.

·      Hooks in functional components, like useEffect(() => {...}, []), are run if they’re specified with an empty dependency array.

 

Example: If you have a UserProfile component that appears when you open a page, it is mounted the first time it’s displayed.

 

2. Rendering

Rendering is the process of creating the component's UI based on its state and props. Rendering happens during both mounting and updating phases, and it doesn’t necessarily mean there’s a visible change in the DOM each time.

 

·      In each render, React evaluates the JSX and generates a virtual representation of the DOM.

·      If there are changes, React uses the virtual DOM to update only the necessary parts of the real DOM.

 

Note: A render can happen for various reasons, like changes in props or state, but it doesn’t guarantee changes in the DOM unless React detects differences.

 

3. Re-rendering

Re-rendering occurs when React re-runs the component function or class’s render method to update the UI in response to changes in state or props.

 

The component doesn’t mount again; it simply updates.

 

When Re-rendering Occurs:

 

·      State Change: If the component’s state changes, React triggers a re-render.

·      Props Change: If a component receives new props from its parent, React re-renders it.

·      Parent Re-render: If the parent component re-renders, child components usually re-render as well, unless specifically optimized with React.memo or shouldComponentUpdate.

 

Example: A Counter component that updates the displayed count when a button is clicked re-renders each time the count changes.

 

4. Remounting

Remounting is when a component is removed from the DOM and then added back as a new instance. When a component remounts, it goes through the entire mounting process again.

 

·      The component’s state is reinitialized to its initial values.

·      Any useEffect hooks with empty dependencies or lifecycle methods (like componentDidMount) run again.

·      React treats it as a brand-new component instance, meaning any old state or effects from the previous instance are discarded.

 

Following are the common reasons for remounting

·      Key Change: If a component’s key prop changes, React considers it a different component and will remount it. This is often used in lists, where changing the key helps React uniquely identify list items.

 

·      Conditional Rendering: If a component is conditionally rendered (e.g., using a boolean state to toggle its visibility), unmounting and remounting occurs each time it’s hidden and shown again.

 

Whenever a remount happens, the old state is completely forgotten by React.

 

Let’s build an application to demonstrate how updating the key property of a component causes it to remount and completely lose its previous state.

 

Step 1:  Go to the post (Quickly Set Up a React Project with Vite: A Step-by-Step), and set up a React Project react-key-change-explorer.

 

Project structure looks like below.


 

 

Step 2: Create components folder in src folder and define CounterChildApp and CounterParentApp components.

 

CounterChildApp.jsx 

import { useState } from "react";

export default function CounterChildApp({ name }) {
  const [count, setCount] = useState(0);

  console.log("Rendered CounterChildApp.....");

  return (
    <div className="counterChildApp">
      <p className="para">Child Application Name : {name}</p>
      <p className="para">Counter : {count}</p>

      <button
        className="button childCounter"
        onClick={() => {
          setCount((prevCount) => prevCount + 1);
        }}
      >
        Increment Counter
      </button>
    </div>
  );
}

CounterParentApp.jsx

import { useState } from "react";
import CounterChildApp from "./CounterChildApp";

let projectNames = ["Nebula", "Orion", "Nexus", "Lyra", "Zypher"];
let projectIndex = -1;
let keyCount = 0;

function getProjectName() {
  if (projectIndex == projectNames.length - 1) {
    projectIndex = -1;
  }

  projectIndex++;
  return projectNames[projectIndex];
}

function updateKey() {
  keyCount++;
}

export default function CounterParentApp() {
  const [pjtName, setPjtName] = useState(getProjectName());
  const [appKey, setAppKey] = useState(keyCount);

  return (
    <div className="container">
      <div className="demoApp">
        <CounterChildApp name={pjtName} key={appKey} />

        <div className="buttonGroup">
          <button
            onClick={() => {
              setPjtName(getProjectName());
            }}
          >
            Update Name
          </button>

          <button
            onClick={() => {
              updateKey();
              setAppKey(keyCount);
            }}
          >
            Update Key
          </button>
        </div>
      </div>
    </div>
  );
}

Step 4: Create styles folder in src folder and define styles.css file.

 

styles.css

.container{
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    width: 100vw;
    height: 100vh;
}

.demoApp{
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    width: 30vw;
    height: 50vh;
    gap: 10px;
    background-color: rgb(222, 222, 200);
    border-radius: 20px;               /* Rounded corners */
    box-shadow: 0 3px 5px rgba(0, 0, 0, 0.1); /* Subtle shadow for depth */
}

.counterChildApp{
    width: 300px;
    height: 250px;
    display: flex;
    flex-direction: column;
    background-color: rgb(244, 193, 204);
    border-radius: 20px;               /* Rounded corners */
    box-shadow: 0 3px 5px rgba(0, 0, 0, 0.1); /* Subtle shadow for depth */
}

.para,
.childCounter{
    padding: 10px;
    font-size: 1.1em;
}

.button{
    width: 200px;
    height: 60px;
    margin: 10px;
}

.buttonGroup{
    display: flex;
    gap: 10px;
}

Step 5: Build and run the application by executing below commands.

npm install
npm run dev

Open the url ‘http://localhost:5173/’ in browser, you will see below screen.


 

Click on the ‘Increment Counter’ button, you will see that the counter value is changed to 1.


 

Click on the button ‘Update Name’ to change the Application Name.

 


You can observe that Application Name is changed from Orion to Nexus, but the state (counter = 1) is still maintained.

 

Now, click on the button ‘Update Key’.



You can observe that the Counter is reinitialized to 0, it is because the component is remounted.

 

You can download the Application from this link.


Previous                                                    Next                                                    Home

No comments:

Post a Comment