ReactJS 中的“this”之谜

IT技术 facebook reactjs react-native react-router
2021-03-27 15:04:50

我对 Facebook 的 React 世界还很陌生。他们的文档似乎非常好,但有几个方面我需要澄清一下。这是其中之一。

源代码:http : //tuts-javascript.appspot.com/reactjs-add-remove-table-row

    var CompanyApp = React.createClass({
    getInitialState: function() {
      return {companylist:this.props.companies};
    },
    handleNewRowSubmit: function( newcompany ) {
      this.setState( {companylist: this.state.companylist.concat([newcompany])} );
    },
    handleCompanyRemove: function( company ) {
      
      var index = -1;   
      var clength = this.state.companylist.length;
        for( var i = 0; i < clength; i++ ) {
            if( this.state.companylist[i].cname === company.cname ) {
                index = i;
                break;
            }
        }
        this.state.companylist.splice( index, 1 );  
        this.setState( {companylist: this.state.companylist} );
    },
    render: function() {
      var tableStyle = {width: '100%'};
      var leftTdStyle = {width: '50%',padding:'20px',verticalAlign: 'top'};
      var rightTdStyle = {width: '50%',padding:'20px',verticalAlign: 'top'};
      return ( 
        <table style={tableStyle}>
          <tr>
            <td style={leftTdStyle}>
              <CompanyList clist={this.state.companylist}  onCompanyRemove={this.handleCompanyRemove}/>
            </td>
            <td style={rightTdStyle}>
              <NewRow onRowSubmit={this.handleNewRowSubmit}/>
            </td>
          </tr>
      </table>
      );
    }
  });
  
  var CompanyList = React.createClass({
    handleCompanyRemove: function(company){
      this.props.onCompanyRemove( company );
    },
    render: function() {
      var companies = [];
      var that = this; // TODO: Needs to find out why that = this made it work; Was getting error that onCompanyDelete is not undefined
      this.props.clist.forEach(function(company) {
        companies.push(<Company company={company} onCompanyDelete={that.handleCompanyRemove} /> );
      });
      return ( 
        <div>
          <h3>List of Companies</h3>
          <table className="table table-striped">
            <thead><tr><th>Company Name</th><th>Employees</th><th>Head Office</th><th>Action</th></tr></thead>
            <tbody>{companies}</tbody>
          </table>
        </div>
        );
    }
  });
  
  var Company = React.createClass({
    handleRemoveCompany: function() {
      this.props.onCompanyDelete( this.props.company );
      return false;
    },
    render: function() {
      return (
        <tr>
          <td>{this.props.company.cname}</td>
          <td>{this.props.company.ecount}</td>
          <td>{this.props.company.hoffice}</td>
          <td><input type="button"  className="btn btn-primary" value="Remove" onClick={this.handleRemoveCompany}/></td>
        </tr>
        );
    }
  });
  
  var NewRow = React.createClass({
    handleSubmit: function() {
      var cname = this.refs.cname.getDOMNode().value;
      var ecount = this.refs.ecount.getDOMNode().value;
      var hoffice = this.refs.hoffice.getDOMNode().value;
      var newrow = {cname: cname, ecount: ecount, hoffice: hoffice };
      this.props.onRowSubmit( newrow );
      
      this.refs.cname.getDOMNode().value = '';
      this.refs.ecount.getDOMNode().value = '';
      this.refs.hoffice.getDOMNode().value = '';
      return false;
    },
    render: function() {
      var inputStyle = {padding:'12px'}
      return ( 
        <div className="well">
          <h3>Add A Company</h3>
        <form onSubmit={this.handleSubmit}>
          <div className="input-group input-group-lg" style={inputStyle}>
            <input type="text"  className="form-control col-md-8"  placeholder="Company Name" ref="cname"/>
          </div>
          <div className="input-group input-group-lg" style={inputStyle}>
            <input type="text"  className="form-control col-md-8" placeholder="Employee Count" ref="ecount"/>
          </div>
          <div className="input-group input-group-lg" style={inputStyle}>
            <input type="text"  className="form-control col-md-8" placeholder="Headoffice" ref="hoffice"/>
          </div>
          <div className="input-group input-group-lg" style={inputStyle}>
            <input type="submit"  className="btn btn-primary" value="Add Company"/>
          </div>
        </form>
        </div>
        );
    }
  });
  var defCompanies = [{cname:"Infosys Technologies",ecount:150000,hoffice:"Bangalore"},{cname:"TCS",ecount:140000,hoffice:"Mumbai"}];
  React.renderComponent( <CompanyApp companies={defCompanies}/>, document.getElementById( "companyApp" ) );

这是 ReactJS 如何工作的一个很好的基本解释。感谢作者。

但是这个评论,

var that = this; // TODO:需要找出原因 = 这使它起作用;收到 onCompanyDelete 不是未定义的错误

为什么这是必要的?这是正确的方法吗?如果不是,那是什么?

提前致谢。

1个回答

ReactJS 特有的“这个”并没有什么神秘之处。

这只是 JavaScript 中回调出现的标准范围问题的一个例子。

当您在 React 组件中时,基础组件上的所有方法都将被限定this为当前组件,就像任何其他 JavaScript“类”一样。

在您的代码片段中,您有一个render方法,它是基础组件上的一个函数,因此this等于组件本身。但是,在该渲染方法中,您使用 调用回调this.props.clist.forEach,该render方法内的任何函数回调都需要绑定到正确的this作用域,或者您可以这样做var that = this(尽管这是一种反模式,应该不鼓励)`。

例如,您的代码段的稍微简化版本:

var MyComponent = React.createClass({
    handleCompanyRemove: function(e) {
        // ...
    },
    render: function() {
        // this === MyComponent within this scope

        this.props.someArray.forEach(function(item) {
            // this !== MyComponent, therefore this.handleCompanyRemove cannot
            // be called!
        })
    }
})

正如您从上面的评论中看到的那样,在您的回调中,.forEach您不能在没有this在外部定义变量或正确绑定函数的情况下直接使用

解决此问题的其他选项是:

将回调函数绑定到正确的 this 范围。例子:

this.props.someArray.forEach(function(item) {
    // this === MyComponent within this scope too now!
    // so you can call this.handleCompanyRemove with no problem
 }.bind(this))

如果你使用 Babel/ES6,你可以使用Fat Arrow函数语法,它保证this作用域继续从父作用域回调。例子:

this.props.someArray.forEach((item) => {
    // this === MyComponent within this scope too now!
    // so you can call this.handleCompanyRemove with no problem
})
确实,但这是特定于 forEach 的 - 我试图对所有回调通用,但使用了 forEach,因为它在他的示例中被 OP 使用。
2021-05-27 15:04:50
@MikeDriver .. 我实际上在使用 babel,在这种情况下,我从未想到过胖箭头功能。再次感谢您指出。
2021-05-30 15:04:50
@MikeDriver .. 非常感谢您的回答。既然您说 var that = this 应该被阻止,那么您是否认为这会导致问题?
2021-06-17 15:04:50
根据MDN,你也可以给forEach一个方面this例如this.props.someArray.forEach(function(item) { }, this) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
2021-06-19 15:04:50
@Sachin 不,它会运行良好。主要原因(对我而言)为什么应该不鼓励这样做是代码可读性,确保您的所有回调都在适当的范围内并且一直使用会更好this当然,如果您已经使用 babel 来转译您的 JSX -> JS,那么 es6 胖箭头函数是最优雅的解决方案。
2021-06-20 15:04:50