How to avoid React componentDidMount being called multiple times
Are you seeing your React component calling the componentDidMount
lifecycle multiple times?
The answer to this problem is to avoid conditional rendering, and avoid adding the React prop key
to the component.
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.
1. 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“.
2. 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!