How to avoid React componentDidMount being called multiple times

Are you seeing your React component calling the componentDidMount lifecycle multiple times?

I’m going to make the assumption you have an understanding of what is React componentDidMount lifecycle and how it works. If you don’t take a look at this article first, “Understanding React componentDidMount and how it works“.

Okay let’s dive into methods to avoid multiple componentDidMount calls.

Avoid conditional rendering

If you perform any type of IF conditional rendering, it will more than likely force your child component to unmount and re-mount.

Thus triggering multiple componentDidMount lifecycle calls.

Here’s a code example:


class ComponentB extends React.Component {
  componentDidMount() {
    console.log("component B mounted");
  }

  render() {
    return <div>Component B</div>;
  }
}

const App = () => {
  const [showCompB, setCompBToggle] = React.useState(false);

  const handleToggle = () => setCompBToggle(!showCompB);

  return (
    <>
      <div>
        <button onClick={handleToggle}>Toggle B component</button>
      </div>
      {showCompB && <ComponentB />}
    </>
  );
};

Code breakdown!

You’ll see in the example that there is a button with a click handler function.

Every time that button gets clicked, it will trigger a queue to update React state variable showCompB.


const [showCompB, setCompBToggle] = React.useState(false);
const handleToggle = () => setCompBToggle(!showCompB);

Okay, now the variable showCompB is being used to determine whether to render <ComponentB />.


{showCompB && <ComponentB />}

The console would look like this every time I showCompB equals true.


component B mounted 
App has mounted 
component B mounted 
component B mounted 

If you want to learn more about conditional rendering, and learn methods to do conditional rendering without calling componentDidMount multiple times, check out this article, “4 React conditional rendering methods with props and state“.

Avoid adding key as a prop

Adding a key prop to your React component may trigger componentDidMount to be called multiple times.

Wait, what’s so special about the key property in React?

What is the key prop and why is it there?

The key property in React is a special property that helps identify HTML elements or a React component in React.

These values have to be very unique to the element or component. Whenever React notices that value has been changed, it will trigger componentWillUnmount the element or component, and re-run componentDidMount.

Here’s an example of how using the key property may cause the componentDidMount lifecycle to be called multiple times.


class ComponentC extends React.Component {
  componentDidMount() {
    console.log("component C mounted");
  }

  render() {
    return <div>Component C</div>;
  }
}

const App = () => {
  const [keyId, incrementKeyValue] = React.useState(0);

  const handleKeyChange = () => incrementKeyValue(keyId + 1);

  return (
    <>
      <div>
        <button onClick={handleKeyChange}>Change C component key</button>
      </div>
      <ComponentC key={keyId} />
    </>
  );
};

In the example above, I’m using React.useState to store a number value.


const [keyId, incrementKeyValue] = React.useState(0);

I also added a handler function to handle the increment of the state variable value.


const handleKeyChange = () => incrementKeyValue(keyId + 1);

Now I’m going to grab the state variable, keyId, and attach it to the component <ComponentC />.


<ComponentC key={keyId} />

Every time you increment the value, React will automatically run componentWillUnmount and follow up with a componentDidMount.

Here’s an example of the console output:


component C mounted
App has mounted
component C mounted 
component C mounted
component C mounted

Conclusion

By default, a React component will only call componentDidMount once. The only way it will get run again is if you delete the component or change the key prop value.

Full Code example

Here’s the sample code you can use to play with


class ComponentB extends React.Component {
  componentDidMount() {
    console.log("component B mounted");
  }

  render() {
    return <div>Component B</div>;
  }
}

class ComponentC extends React.Component {
  componentDidMount() {
    console.log("component C mounted");
  }

  render() {
    return <div>Component C</div>;
  }
}

class ComponentD extends React.Component {
  componentDidMount() {
    console.log("component D mounted");
  }

  render() {
    return <div>Component D</div>;
  }
}

const App = () => {
  const [showCompB, setCompBToggle] = React.useState(false);
  const [keyId, incrementKeyValue] = React.useState(0);

  React.useEffect(() => console.log("App has mounted"), []);

  const handleToggle = () => setCompBToggle(!showCompB);

  const handleKeyChange = () => incrementKeyValue(keyId + 1);

  return (
    <>
      <div>
        <button onClick={handleToggle}>Toggle B component</button>
        <button onClick={handleKeyChange}>Change C component key</button>
      </div>
      {showCompB && <ComponentB />}
      <ComponentC key={keyId} />
      <ComponentD />
    </>
  );
};

Oh wow, you’ve made it this far! If you enjoyed this article perhaps like or retweet the thread on Twitter:

I like to tweet about React and post helpful code snippets. Follow me there if you would like some too!