无法访问事件处理程序中的React实例(this)
我在ES6(用BabelJS)写一个简单的组件,并且函数this.setState
不起作用。
典型的错误包括像
无法读取未定义的属性“setState”
要么
this.setState不是一个函数
你知道为什么吗? 这里是代码:
import React from 'react' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: 'startValue'} } sendContent(e) { console.log('sending input content '+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={this.sendContent}>Submit</button> </div> ) } } export default SomeClass
this.changeContent
需要在作为onChange
prop传递之前通过this.changeContent.bind(this)
绑定到组件实例,否则函数体中的this
variables不会引用组件实例,而是引用到window
。 请参阅函数::绑定 。
当使用React.createClass
而不是ES6类时,组件上定义的每个非生命周期方法都会自动绑定到组件实例。 请参阅自动绑定 。
请注意,绑定一个函数会创build一个新函数。 您可以直接在渲染中绑定它,这意味着每次组件渲染时都会创build一个新函数,或者在您的构造函数中绑定它,只会触发一次。
constructor() { this.changeContent = this.changeContent.bind(this); }
VS
render() { return <input onChange={this.changeContent.bind(this)} />; }
Refs在组件实例上设置,而不是在React.refs
:您需要将React.refs.someref
更改为this.refs.someref
。 您还需要将sendContent
方法绑定到组件实例,以便引用它。
莫豪斯是正确的,但这可以解决没有bind
。
您可以将箭头function与类属性提议一起使用:
class SomeClass extends React.Component { changeContent = (e) => { this.setState({inputContent: e.target.value}) } render() { return <input type="text" onChange={this.changeContent} />; } }
由于箭头函数是在构造函数的范围内声明的,并且由于箭头函数从它们的声明范围中保留了this
,所以这一切都是可行的。 这里的缺点是这些不会是原型上的函数,它们都将被重新创build与每个组件。 然而,这并不是什么坏处,因为bind
导致了相同的事情。
当从React.createClass()
组件定义语法转换到扩展React.Component
的ES6类方法时,这个问题是我们大多数人经历的第一件事情React.Component
。
这是由React.createClass()
与extends React.Component
的this
上下文差异引起的 。
使用React.createClass()
会自动绑定this
上下文(值),但是这不是使用ES6类的情况。 当这样做ES6的方式(通过扩展React.Component
) this
上下文默认为null
。 类的属性不会自动绑定到React类(组件)实例。
解决这个问题的方法
我总共知道4种一般方法。
-
在你的类的构造函数中绑定你的函数 。 被许多人认为是避免触及JSX的最佳实践方法,并且不会在每个组件重新渲染上创build新的function。
class SomeClass extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } }
-
在线绑定你的函数 。 你仍然可以在一些教程/文章/等等中find这种方法,所以重要的是你意识到这一点。 它像#1一样的概念,但要注意绑定一个函数为每个重新渲染创build一个新的函数。
class SomeClass extends React.Component { handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick.bind(this)}></button> ); } }
-
使用胖箭头function 。 直到箭头函数,每个新函数都定义了它自己的
this
值。 但是,箭头函数不会创build它自己的this
上下文,所以this
具有来自React组件实例的原始含义。 因此,我们可以:class SomeClass extends React.Component { handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={ () => this.handleClick() }></button> ); } }
要么
class SomeClass extends React.Component { handleClick = () => { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } }
-
使用实用函数库来自动绑定你的function 。 那里有几个实用程序库,它会自动为你做这项工作。 这里有一些stream行的,只是提到一些:
-
Autobind装饰器是一个NPM包,它将一个类的方法绑定到这个正确的实例上,即使这些方法是分离的。 该包在方法之前使用
@autobind
将其绑定到组件上下文的正确引用 。import autobind from 'autobind-decorator'; class SomeClass extends React.Component { @autobind handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } }
Autobind装饰器足够聪明,可以让我们像组件类一样绑定组件类中的所有方法,就像方法1一样。
-
Class Autobind是另一个被广泛用于解决这个绑定问题的NPM包。 与Autobind装饰器不同,它不使用装饰器模式,但实际上只是在构造函数中使用了一个函数 , 该函数自动将 Component的方法绑定到这个的正确引用上。
import autobind from 'class-autobind'; class SomeClass extends React.Component { constructor() { autobind(this); // or if you want to bind only only select functions: // autobind(this, 'handleClick'); } handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } }
PS:其他非常相似的库是React Autobind 。
-
build议
如果我是你,我会坚持方法#1。 但是,只要你在类的构造函数中得到了很多的绑定,我build议你去探索方法#4中提到的一个辅助类库。
其他
这与你的问题没有关系,但是你不应该过度使用文献 。
你的第一个倾向可能是在你的应用程序中使用引用“让事情发生”。 如果是这种情况,请花一点时间,更仔细地考虑组件层次结构中应该拥有哪些状态。
为了类似的目的,就像你需要的那样,使用受控组件是首选的方法。 我build议你考虑使用你的组件state
。 所以,你可以简单地访问这样的值: this.state.inputContent
。
我们需要将事件函数与构造函数中的组件绑定如下,
import React from 'react' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: 'startValue'} this.changeContent = this.changeContent.bind(this); } sendContent(e) { console.log('sending input content '+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={this.sendContent}>Submit</button> </div> ) } } export default SomeClass
谢谢
发生此问题是因为this.changeContent
和onClick={this.sendContent}
未绑定到组件的实例。
还有另一种解决scheme(除了在构造函数()中使用bind())来使用ES6的箭头函数,它们共享周围代码的相同词法范围并维护它 ,因此您可以将render()中的代码更改为是:
render() { return ( <input type="text" onChange={ () => this.changeContent() } /> <button onClick={ () => this.sendContent() }>Submit</button> ) }
你好,如果你不想在绑定自己的函数调用。 你可以使用'class-autobind'并像那样导入它
import autobind from 'class-autobind'; class test extends Component { constructor(props){ super(props); autobind(this); }
在超级调用之前不要写自动绑定,因为它不会工作
如果你想保持构造函数语法的绑定,你可以使用提议绑定运算符和转换你的代码如下:
constructor() { this.changeContent = ::this.changeContent; }
代替 :
constructor() { this.changeContent = this.changeContent.bind(this); }
简单得多,不需要bind(this)
或者fatArrow
。
你可以通过三种方式解决这个问题
1.在构造函数中绑定事件函数如下
import React from 'react' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: 'startValue'} this.changeContent = this.changeContent.bind(this); } sendContent(e) { console.log('sending input content '+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={this.sendContent}>Submit</button> </div> ) } } export default SomeClass
绑定时被调用
import React from 'react' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: 'startValue'} } sendContent(e) { console.log('sending input content '+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={this.sendContent.bind(this)}>Submit</button> </div> ) } } export default SomeClass
3.使用箭头function
import React from 'react' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: 'startValue'} } sendContent(e) { console.log('sending input content '+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={()=>this.sendContent()}>Submit</button> </div> ) } } export default SomeClass
- ES6 / 2015中的无担保财产访问(和有条件分配)
- Webpack babel 6 ES6装饰器
- Babel文件被复制而不被转换
- 在同构React组件中导入CSS文件
- 使用Webpack和Babel,你可能需要一个合适的加载器来处理这个文件types
- babel CLI复制nonjs文件
- babel-loader jsx SyntaxError:意外的标记
- 为什么在使用babel-loader的时候Object.assign()需要一个polyfill?
- Webpack / Babel / React生成错误:“未知选项:foo / node_modules / react / react.js.Children”