Home > Software design >  React useEffect hooks return () => cleanup() vs return cleanup
React useEffect hooks return () => cleanup() vs return cleanup

Time:09-02

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",
    ...
  }
}

CodePudding user response:

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.

CodePudding user response:

Because you're direct returning view.destroy() from useEffect return statement. But it should be called from a callback function.

useEffect(() => {
....
    // return view.destroy raises Unhandled Runtime Error

     ***this should be returned from a callback function. 
     like this*** 

    **return () => view.destroy()**
....
}
  • Related