React跨组件传递方法时this指向异常无法调用setState如何解决
React类组件父组件方法this指向异常问题
问题复现
在类组件实现的React应用中,子组件Component1需要调用父组件App中定义的方法,实际运行时方法内无法访问App组件实例的this上下文,调用this.setState时报错,复现代码如下:
class Component1 extends React.Component { render() { return ( <div> <h5>sub Component 1 </h5> <button onClick={() => this.props.data[0].action("foo")}> Click Me! </button> </div> ); } } class App extends React.Component { constructor(props) { super(props); this.state = { clicked: false }; this.onClickMe = this.onClickMe.bind(this); } data = [{ name: "Giordano", action: this.onClickMe }]; onClickMe(value) { console.log(value); // 此处this指向data数组中的对象,而非App组件实例,无setState方法 this.setState({ clicked: true }); } render() { return ( <div> <h1>Main page</h1> <Component1 data={this.data} /> </div> ); } }
问题原因
这个问题是类实例字段初始化顺序+函数调用时this默认绑定规则共同导致的:
- ES6类的实例字段初始化时机在
super()调用完成后、constructor内剩余代码执行前。代码中data是类实例字段,初始化时给action赋值的this.onClickMe,是定义在类原型上未绑定this的原始方法——此时constructor内的this.onClickMe = this.onClickMe.bind(this)还未执行,实例上还没有绑定好this的方法副本。 - 子组件触发调用时,是通过
this.props.data[0].action("foo")的形式调用方法,这种对象方法调用形式会将函数内的this默认绑定到data[0]这个普通对象上,因此方法内访问不到App组件实例上的setState方法。
修复方案
任选以下一种方案即可修复:
- 方案1:调整初始化顺序,将
data的赋值移到constructor内bind操作之后,确保data中存储的是已经绑定好this的方法实例
class App extends React.Component { constructor(props) { super(props); this.state = { clicked: false }; // 先完成方法绑定 this.onClickMe = this.onClickMe.bind(this); // 绑定完成后再初始化data this.data = [{ name: "Giordano", action: this.onClickMe }]; } onClickMe(value) { console.log(value); this.setState({ clicked: true }); } render() { return ( <div> <h1>Main page</h1> <Component1 data={this.data} /> </div> ); } }
- 方案2:将类方法改为箭头函数形式的实例字段,无需手动bind,初始化时自动绑定组件实例this
class App extends React.Component { constructor(props) { super(props); this.state = { clicked: false }; } // 箭头函数定义实例方法,自动绑定当前实例this onClickMe = (value) => { console.log(value); this.setState({ clicked: true }); } // 此时data初始化时拿到的onClickMe已经是绑定好this的方法 data = [{ name: "Giordano", action: this.onClickMe }]; render() { return ( <div> <h1>Main page</h1> <Component1 data={this.data} /> </div> ); } }
- 方案3:在
data中定义action时包裹一层箭头函数,调用时始终以App实例为上下文触发目标方法
class App extends React.Component { constructor(props) { super(props); this.state = { clicked: false }; this.onClickMe = this.onClickMe.bind(this); } // action包裹箭头函数,内部调用时this指向App实例 data = [{ name: "Giordano", action: (val) => this.onClickMe(val) }]; onClickMe(value) { console.log(value); this.setState({ clicked: true }); } render() { return ( <div> <h1>Main page</h1> <Component1 data={this.data} /> </div> ); } }
内容的提问来源于stack exchange,提问作者DennyHiu
相关产品推荐
相关产品推荐

