React useEffect中return cleanup与return ()=>cleanup()的差异及报错解析
React useEffect清理CodeMirror实例的回调差异问题解析
问题场景
在基于CodeMirror ^6.0.1开发React自定义Hook时,遇到useEffect清理回调的异常:
- 直接返回
view.destroy会触发错误:Unhandled Runtime Error TypeError: Cannot read properties of undefined (reading 'plugins') - 返回
() => view.destroy()则能正常完成实例销毁
自定义Hook代码:
export const useCodeMirror = () => { const state = ... const parent = ... useEffect(() => { const view = new EditorView({state: state, parent: parent}) // 报错写法 // return view.destroy // 正常写法 return () => view.destroy() }, [element]) }
报错原因分析
核心差异在于函数调用时的this指向:
- 当直接返回
view.destroy时,React执行清理回调时是将这个函数作为独立函数调用的。在React默认启用的严格模式下,独立调用的函数this指向undefined。而CodeMirror的destroy方法内部依赖this指向EditorView实例来访问plugins、inputState等属性,因此会抛出属性读取失败的错误。 - 当返回
() => view.destroy()时,view.destroy()是通过对象方法调用的形式执行,此时this会自动绑定到view实例上,destroy方法能正常访问实例的所有属性,因此可以顺利执行销毁逻辑。
CodeMirror destroy方法源码佐证
CodeMirror的destroy方法源码(来自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; }
方法内多处使用this访问实例属性,一旦this指向错误,必然导致属性读取失败。
结论
在React useEffect的清理回调中,若要调用类实例的方法,不能直接返回方法引用,必须通过函数包裹的形式调用,确保方法执行时this指向正确的实例对象。
内容的提问来源于stack exchange,提问作者Wt.N
相关产品推荐
相关产品推荐

