React Quotes组件Flux Store回调绑定错误:this指向异常求助
this指向异常无法调用setState的问题 嘿,这个问题我在Flux项目里也踩过坑!核心原因是:当QuoteStore触发newQuoteAdded事件时,getQuotes作为回调函数被调用,此时它内部的this并没有指向你的组件实例,而是指向了事件触发的上下文(严格模式下甚至是undefined),所以才会出现this.setState调用失败的情况。
下面给你几种实用的解决方案,按推荐程度排序:
1. 最简洁:把getQuotes定义为类属性箭头函数
如果你用的是ES6+的类组件语法,直接把方法写成箭头函数形式,它会自动绑定组件实例的this,全程不用额外操作:
class Quotes extends React.Component { // 用箭头函数定义方法,this永久绑定组件实例 getQuotes = () => { console.log(this); // 现在this指向组件实例 this.setState({ quotes: QuoteStore.getQuotes() }); } componentWillMount(){ QuoteStore.on("newQuoteAdded", this.getQuotes); } componentWillUnmount(){ QuoteStore.removeListener("newQuoteAdded", this.getQuotes); } }
这种写法最省心,也是现代React项目里最常用的方式之一。
2. 稳妥可靠:在构造函数中提前绑定this
如果你的项目还在使用传统的类方法定义,在组件的构造函数里提前用bind绑定this,确保整个生命周期里方法的引用是固定的,卸载监听时也不会出问题:
class Quotes extends React.Component { constructor(props) { super(props); // 提前绑定this到getQuotes方法 this.getQuotes = this.getQuotes.bind(this); } getQuotes() { console.log(this); this.setState({ quotes: QuoteStore.getQuotes() }); } componentWillMount(){ QuoteStore.on("newQuoteAdded", this.getQuotes); } componentWillUnmount(){ QuoteStore.removeListener("newQuoteAdded", this.getQuotes); } }
这里要注意:不要在componentWillMount里直接bind(比如this.getQuotes.bind(this)),因为每次bind都会返回一个新的函数,这样卸载时你移除的是另一个新函数,导致监听无法正确清除。
3. 灵活应急:用箭头函数包裹回调
如果只是临时解决问题,也可以在绑定监听时用箭头函数包裹你的方法,箭头函数会自动捕获当前上下文的this:
class Quotes extends React.Component { componentWillMount(){ // 用箭头函数包裹,继承外层的this(组件实例) this.quoteListener = () => this.getQuotes(); QuoteStore.on("newQuoteAdded", this.quoteListener); } componentWillUnmount(){ // 必须用同一个引用移除监听,所以要把箭头函数存起来 QuoteStore.removeListener("newQuoteAdded", this.quoteListener); } getQuotes() { console.log(this); this.setState({ quotes: QuoteStore.getQuotes() }); } }
这种方式要注意:一定要把箭头函数的引用存在组件实例上,不然卸载时找不到对应的监听函数,会导致内存泄漏。
补充:为什么会出现这个问题?
JavaScript里函数的this指向是由调用方式决定的,而不是定义位置。当QuoteStore触发事件调用getQuotes时,它是作为普通函数被调用的,此时this默认指向全局对象(非严格模式)或undefined(严格模式),而不是你的组件实例。上面的几种方案都是通过不同方式强制把this绑定到组件实例上,从而解决这个问题。
内容的提问来源于stack exchange,提问作者Avinash Dangi

