I have a problem understanding why in some cases componentWillUnmount
method is not called even if the component I would expect is unmounted.
To be more concrete, this is the example. Let's consider a Parent component with a button and 2 children: Child_Odd and Child_Even. Child_Odd is "shown" if the number of clicks of the button is odd, otherwise Child_Even is "shown".
I would expect to see the the componentWillUnmount
method to be called when the component "disappears", but on the contrary this does not happen. Here a stackblitz reproducing the case (this stackblitz includes also a similar case where the componentWillUnmount
method is actually called).
This is the relevant code
export class ChildFlipped extends React.Component {
render() {
return (
<div>
{this.props.name} - No of clicks ({this.props.numberOfClicks})
</div>
);
}
}
export class ParentFlips extends React.Component {
constructor(props: any) {
super(props);
this.state = {
clickCounter: 0,
};
}
render() {
return (
<div>
<button
onClick={() => this.updateState()}
>
Click me
</button>
{this.state.clickCounter % 2 ? (
<ChildFlipped
name={"Even"}
numberOfClicks={this.state.clickCounter}
></ChildFlipped>
) : (
<ChildFlipped
name={"Odd"}
numberOfClicks={this.state.clickCounter}
></ChildFlipped>
)}
</div>
);
}
updateState() {
this.setState((prevState, _props) => ({
clickCounter: prevState.clickCounter + 1,
}));
}
}
It doesn't fire because the component remains mounted. With respect to reconciliation, the conditional expression
this.state.clickCounter % 2
? <ChildFlipped name={"Even"} numberOfClicks={this.state.clickCounter}/>
: <ChildFlipped name={"Odd"} numberOfClicks={this.state.clickCounter}/>
is indistinguishable from
<ChildFlipped name={this.state.clickCounter % 2? "Even" : "Odd"}
numberOfClicks={this.state.clickCounter}/>
since both clauses refer to the same ChildFlipped
component. If you add keys, you can make them distinguishable and trigger unmounting:
his.state.clickCounter % 2
? <ChildFlipped key='even' name={"Even"} numberOfClicks={this.state.clickCounter}/>
: <ChildFlipped key='odd' name={"Odd"} numberOfClicks={this.state.clickCounter}/>
For experimenting with lifecycle methods I built a React lifecycle visualizer (StackBlitz) which may be useful for you.