I have a question about useEffect
's cleanup callback function. In one word, return cleanup
gives an error, but return () => cleanup()
works well. Yes, they are different, but how are they different? Does anyone know how to explain? Thanks,
I came across this question while I am writing with codemirror.
The error message I see is
Unhandled Runtime Error
TypeError: Cannot read properties of undefined (reading 'plugins')
My hooks
export const useCodeMirror = () => {
const state = ...
const parent = ...
useEffect(() => {
const view = new EditorView({state: state, parent: parent})
// return view.destroy raises an error
return () => view.destroy() // works perfectly
}, [element])
}
node_modules/@codemirror/view/dist/index.js L:6740-6750
destroy() {
for (let plugin of this.plugins)
plugin.destroy(this);
this.plugins = [];
this.inputState.destroy();
this.dom.remove();
this.observer.destroy();
if (this.measureScheduled > -1)
cancelAnimationFrame(this.measureScheduled);
this.destroyed = true;
}
package.json
{
"dependencies": {
...
"codemirror": "^6.0.1",
...
}
}
It's normally fine to return a cleanup function directly, rather than wrapping it in an additional arrow function. The only reason the additional function is needed in this case because destroy
uses this
.
For regular functions, the value of this
is determined by how the function is called. So if you have code that says view.destroy()
, then the characters view.
are the reason that this
gets set to view
. That's why () => view.destroy()
one works: you are explicitly saying what this
should be, as you call the function.
But if you just return view.destroy
, you are not calling the function, just returning a reference of it to react. React doesn't know anything about view
, it just knows you returned a function. So when react later calls your function, it doesn't know what to set this
to, and so this
gets set to undefined. Since it's undefined, this.plugins
causes the error you see.