Problem:
There is a basic vanilla js component that should be executed when element is added to DOM (componentDidMount) and destroyed when removed. Here is a basic example of such component:
class TestComponent {
interval?: number;
constructor() {
this.interval = window.setInterval(() => {
console.log('interval is ticking')
}, 1000);
}
destroy() {
window.clearInterval(this.interval);
}
}
I add it via React like so:
function TestApp() {
const [testComponent, setTestComponent] = React.useState<TestComponent|null>(null);
React.useEffect(() => {
// initialize component when it's added to dom
const localTestComponent = new TestComponent();
setTestComponent(localTestComponent);
return () => {
// destroy component when it's removed
if (testComponent) {
testComponent.destroy();
}
};
}, []);
return (
<div>Test app</div>
);
}
For some reason it initializes twice, but never destroys (I can tell because “interval is ticking” message appears twice each second, but it should tick only once per second). Any idea why this is happening?
The destroy()
method is never reached because if (testComponent) {
is null
in the cleanup phase.
I tried placing this component at the very root of my app and removing all third-party code, but nothing seems to help.
Solution:
Not everything should be state.
When you call setTestComponent(localTestComponent)
, it requires a rerender in order to change the value of testComponent
. In your effect testComponent
is still null
. You already have the value in localTestComponent
, just use it instead of testComponent
. You don’t need state for that
React.useEffect(() => {
const localTestComponent = new TestComponent();
return localTestComponent.destroy
}, []);