绑定到React组件的函数无法传递给子组件和重新绑定

时间:2018-02-23 16:01:30

标签: javascript reactjs

我在这个上下文问题上遇到了麻烦:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class Foo extends Component {
  constructor(props) {
    super(props)
    this.state = {
      error: false
    }
  }

  render() {
    const { handleChange } = this.props;
    let _handleChange = handleChange.bind(this);
    return (
      <div>
        <input type='file' onChange={_handleChange} id='input'/>
        <label htmlFor='input'> Upload </label>
        {this.state.error && <span>{this.state.errorMessage}</span>}
      </div>
    )
  }
}


class FooClosure extends Component {
  handleChange = (event) => {
    let self = this // this will always be FooClosure
    debugger // local this context will be Foo
    this.setState({ error: true, errorMessage: 'Some error happened'})
  }
  render() {
    return (
      <div>
        <Foo handleChange={this.handleChange} />
      </div>
    )
  }
}

class App extends Component {
  render() {
    return (
      <div className="App">
        <FooClosure />
      </div>
    );
  }
}

export default App;

我正在父组件中定义的函数内部尝试setState,该组件在子组件中被绑定。我对Javascript的理解意味着this将成为子组件,但this.setState总是在FooClosure的上下文中执行。谁能帮我理解为什么会这样?

我可以通过定义handleChange = (event, self) => ...来轻松解决这个问题,但我认为我不应该这样做。

3 个答案:

答案 0 :(得分:1)

这就是你应该写它的方式。

class Foo extends Component {
  render() {
    return (
      <div>
        <input type='file' onChange={this.props.handleChange} id='input'/>
        <label htmlFor='input'> Upload </label>
        {this.props.error && <span>{this.props.errorMessage}</span>}
      </div>
    )
  }
}


class FooClosure extends Component {
  constructor(props) {
    super(props);
    this.state = {error: false, errorMessage: ''};
  }

  handleChange = (event) => {
    this.setState({ error: true, errorMessage: 'Some error happened'})
  }

  render() {
    return (
      <div>
        <Foo handleChange={this.handleChange.bind(this)} error={this.state.error} errorMessage={this.state.errorMessage} />
      </div>
    )
  }
}

答案 1 :(得分:0)

当您在this方法中定义handleChange时,您明确告诉程序要查看其正在编写函数的当前范围。将其作为道具传递只会在其书面范围内运行该功能,而不是重新定义this,也不会在Foo课程中重新定义该功能。

想象一下FooClosureFoo在两个独立的房间里,不知何故变成众生。 FooClosure有能力制作比萨饼。 Foo能够致电FooClosure并要求制作比萨饼。这里的问题是Foo要求FooClosure制作披萨,但是所有的配料和唯一的工作烤箱都在Foo的房间里。

在这种情况下,最好的办法是将handleChange方法转移到Foo课程中,以便this可以引用Foo而不是FooClosure反过来允许您设置Foo的状态。

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class Foo extends Component {
  constructor(props) {
    super(props)
    this.state = {
      error: false
    }
  }

  handleChange = event => {
    this.setState({ 
      error: true, 
      errorMessage: 'Some error happened',
    })
  }

  render() {
    const { handleChange } = this
    return (
      <div>
        <input type='file' onChange={handleChange} id='input'/>
        <label htmlFor='input'> Upload </label>
        {this.state.error && <span>{this.state.errorMessage}</span>}
      </div>
    )
  }
}


class FooClosure extends Component {
  render() {
    return (
      <div>
        <Foo />
      </div>
    )
  }
}

class App extends Component {
  render() {
    return (
      <div className="App">
        <FooClosure />
      </div>
    );
  }
}

export default App;

现在这当然会使你的FooClosure课程变得多余。如果你需要根据Foo中的onChange来处理任何事情,你可以使用它:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class Foo extends Component {
  constructor(props) {
    super(props)
    this.state = {
      error: false
    }
  }

  handleChange = event => {
    const { handleFooClosureMethod } = this.props.handleFooClosureMethod
    handleFooClosureMethod(args)
    this.setState({ 
      error: true, 
      errorMessage: 'Some error happened',
    })
  }

  render() {
    const { handleChange } = this
    return (
      <div>
        <input type='file' onChange={handleChange} id='input'/>
        <label htmlFor='input'> Upload </label>
        {this.state.error && <span>{this.state.errorMessage}</span>}
      </div>
    )
  }
}


class FooClosure extends Component {
  handleChange = args => {
    //handle this method
  }
  render() {
    return (
      <div>
        <Foo handleFooClosureMethod={this.handleChange} />
      </div>
    )
  }
}

class App extends Component {
  render() {
    return (
      <div className="App">
        <FooClosure />
      </div>
    );
  }
}

export default App;

请记住,每次在Foo课程中发生onChange事件时,后者都会触发。

答案 2 :(得分:0)

这很有帮助,但并不完全准确。问题在于,通过绑定它或将其建立为匿名函数,它不能反弹到Foo的新上下文。

class Foo extends Component {
  constructor(props) {
    super(props)
    this.state = {
      error: false
    }
    this.handleChange = this.props.handleChange.bind(this)
    debugger
  }

  render() {
    console.log('foo state', this.state)
    return (
      <div>
        <input type='file' onChange={this.handleChange} id='input'/>
        <label htmlFor='input'> Upload </label>
        {this.state.error && <span>{this.state.errorMessage}</span>}
      </div>
    )
  }
}


class FooClosure extends Component {
  constructor(props) {
    super(props)
    this.handleChange = this.handleChange;
  }
  handleChange() {
    let self = this // this will now be Foo
    debugger // local this context will be Foo
    this.setState({ error: true, errorMessage: 'Some error happened'})
  }
  render() {
    console.log('closure state', this.state)
    return (
      <div>
        <Foo handleChange={this.handleChange} />
      </div>
    )
  }
}