函数未传递给 Button 的原因是什么

What is the reason that function is not passed to a Button

我知道 JavaScript 的作用域,但可能我不完全理解它们,因为这段代码不起作用。

此代码使用 React 和 Relay Modern 框架。

有 2 个按钮,第一个在 queryRender 内,传递给 Relay Modern QueryRenderer,第二个在后面(参见函数 render)。第二个正在运行,第一个不执行 clickTest 函数。 (这是实际代码的简化版本)

class Candidates extends Component {
  static propTypes = {
    viewer: PropTypes.object
  }

  constructor (props) {
    super(props)
    this.clickTest = this.clickTest.bind(this)
  }

  clickTest () {
    console.log('click works')
  }    

  queryRender ({error, props}) {
    if (error) {
      return <pre>{error.message}</pre>
    } else if (props) {
      return (
        <div>
          <Button onClick={this.clickTest}>this DOESN'T work</Button>
        </div>
      )
    }
    return <Loader active>Loading...</Loader>
  }

  render () {
    return (
      <div>
        <QueryRenderer
          environment={environment}
          query={query} 
          render={this.queryRender}
        />
        <Button onClick={this.clickTest}>this works</Button>
      </div>
    )
  }
}

query 变量已定义,我只是没有将其包含在该摘录中。

当我用匿名函数替换第一个按钮的 onClick 函数时

<Button onClick={() => this.clickTest()}>this DOESN'T work</Button>

然后我得到这样的错误:Uncaught TypeError: _this2.clickTest is not a function

任何人都可以向我解释为什么这段代码会这样运行吗?

在 javascript 中,this 的含义不是在 创建函数 时确定的,而是在 时确定的调用。当 QueryRenderer 调用您的 queryRender 函数时,它不知道它需要在您的 class 的上下文中调用它,因此 this 不会引用您认为它所引用的内容。

您要么需要绑定 queryRender 函数,就像您在构造函数中使用 clicktest 函数一样,要么您需要重新设计 queryRender,这样它就不需要引用 this.

箭头函数不会创建新的作用域,它的作用域包含执行上下文,在这种情况下,它是 QueryRenderer 作用域,您没有此函数。当您将它作为简单函数传递时,范围将是未定义的,我不知道 Button 在里面做了什么。我没有使用过 Rely,不确定您是否可以从 Rely render 方法中引用组件。

要扩展 Artur 和 Nicholas 的答案,您需要 bind() this 或使用箭头函数来确保 this 指的是组件本身。你已经有了 bind 方法,这里是一个箭头函数的例子,它不需要绑定,因为箭头函数实际上并不绑定 this 值,它们使用它们的父作用域......

class Candidates extends Component {
  static propTypes = {
    viewer: PropTypes.object
  }

  constructor (props) {
    super(props)
    this.clickTest = this.clickTest.bind(this)
  }

  clickTest () {
    console.log('click works')
  }    

  queryRender = ({error, props}) => {
    if (error) {
      return <pre>{error.message}</pre>
    } else if (props) {
      return (
        <div>
          <Button onClick={this.clickTest}>this DOESN'T work</Button>
        </div>
      )
    }
    return <Loader active>Loading...</Loader>
  }

  render () {
    return (
      <div>
        <QueryRenderer
          environment={environment}
          query={query} 
          render={this.queryRender}
        />
        <Button onClick={this.clickTest}>this works</Button>
      </div>
    )
  }
}