React:无法在返回JSX

时间:2017-08-23 14:57:35

标签: reactjs

我正在学习React,尝试使用浏览器HTML5文件API创建一个简单的Tweetbox“上传”多个图像。

见下面的截图:

enter image description here

正如官方文档中所建议的那样,我使用内置的map函数来遍历数组中上传的图片网址。

我将它包含在一个变量中,以便它可以在包装器div中的下面的JSX中重用,我只想在上传图像时显示它。

问题在于,由于某种范围问题,它无法“看到”或“访问”我的_removePhoto函数并点击此代码段中的处理程序:

_removePhoto(event) { 
    let array = this.state.imagePreviewUrls;
    let index = array.indexOf(event.target.result)
      array.splice(index, 1);
      this.setState({imagePreviewUrls: array });
      // this.setState( {imagePreviewUrls: false} );
  }

  render() {  

    let {imagePreviewUrls} = this.state;
    let {thereIsImage} = this.state;
    // var {thereIsImage} = this.state;
    // onClick={this._removePhoto}

    var imageList = imagePreviewUrls.map(function(value, index) {
        return (
          <figure className="ma0 relative flex items-center justify-center">
              <button onClick={this._removePhoto} className="button-reset pointer dim bn bg-black h2 w2 br-100 white flex items-center justify-center absolute absolute--fill-l center"><i className="material-icons f5">close</i>
              </button>
              <img key={index} src={value} className="h3 w3" />
          </figure>
          )
    })

这给出了错误:

Uncaught TypeError: Cannot read property '_removePhoto' of undefined
    at bundle.js:19085
    at Array.map (<anonymous>)

(如果我没有以这种方式将map函数括在imageList变量中,则_removePhoto逻辑有效。)

此外,如果我删除onClick事件处理程序,它会显示图像预览,但控制台中还有另一个错误,说我需要包含“index”。但是,我已根据文档的说明将其包括在内。

以下是我的完整代码:

import React from 'react'
import ReactDOM from 'react-dom'
// This library didn't help me solve the problem
// const classNames = require('classnames');

class TweetBox extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
          text: "", 
          files: [],
          imagePreviewUrls: [],
          thereIsImage: false
    };

    this._handleTextChange = this._handleTextChange.bind(this);
    this._triggerFileDialogue = this._triggerFileDialogue.bind(this);
    this._handlePhotoUpload = this._handlePhotoUpload.bind(this);
    this._removePhoto = this._removePhoto.bind(this);
  } 


  _removePhoto(event) { 
    let array = this.state.imagePreviewUrls;
    let index = array.indexOf(event.target.result)
      array.splice(index, 1);
      this.setState({imagePreviewUrls: array });
      // this.setState( {imagePreviewUrls: false} );
  }

  render() {  

    let {imagePreviewUrls} = this.state;
    let {thereIsImage} = this.state;
    // var {thereIsImage} = this.state;
    // onClick={this._removePhoto}

    var imageList = imagePreviewUrls.map(function(value, index) {
        return (
          <figure className="ma0 relative flex items-center justify-center">
              <button onClick={this._removePhoto} className="button-reset pointer dim bn bg-black h2 w2 br-100 white flex items-center justify-center absolute absolute--fill-l center"><i className="material-icons f5">close</i>
              </button>
              <img key={index} src={value} className="h3 w3" />
          </figure>
          )
    })

    return (
      <div>

        {/* TITLE SECTION */}
          <div className="pv2 tc bb b--black-10">
            <h1 className="ma0 f5 normal">Create New Report</h1>
          </div>

        <div className="bg-near-white pa3">

        <textarea onChange={this._handleTextChange} placeholder="Write your report here" name="tweet" rows="4" className="w-100 br2 ba b--black-10 pa2"></textarea>

        { imagePreviewUrls.length > 0 &&
        <div className="bg-black-10 pa2 flex"> 
          {imageList}          
        </div>
      } 

        <input className="hide" ref={ (input) => { this.fileInput = input; } } onChange={ this._handlePhotoUpload } type="file"></input>

        <div className="mt3 flex justify-between">
            <button onClick={ this._triggerFileDialogue } className="button-reset flex items-center br2 bn bg-transparent blue hover-bg-black-10 pointer">
              <i className="material-icons f3">photo_camera</i>
            </button>

          <div className="flex items-center">
            <button disabled={ this.state.text.length === 0 } className="button-reset bg-blue bn white f6 fw5 pv2 ph3 br2 dim">Message</button>
          </div>
        </div>

        {/* End "near-white" subcontainer */}
        </div> 
      {/* End "b--black-10" parent container */}
      </div> 
    ); 

} // End Render    

  _handleTextChange(event) {
      this.setState( { text: event.target.value } );
      // For debugging:
      // console.log( { text: event.target.value })
    }

  _triggerFileDialogue(event) {
    this.fileInput.click();
  }

  _handlePhotoUpload(event) {
    // var self = this;
    event.preventDefault();

    let files = event.target.files;

    for (let i = 0; i < files.length; i++) {
      let reader = new FileReader();      

      reader.onloadend = (evt) => {

        var imageString = evt.target.result;
        let updatedImages = this.state.imagePreviewUrls.concat(imageString);

        this.setState({
          imagePreviewUrls: updatedImages,
          files: files[i],
          thereIsImage: true
        });

      }
      reader.readAsDataURL(files[i]);
      console.log(files[i]);
    }

  }

} // End Master React Class


ReactDOM.render( <TweetBox />, document.getElementById("app") );

非常感谢,我已经在Stack Overflow上搜索了很多东西,但找不到解决方案!上面的代码应该是可重现的。

1 个答案:

答案 0 :(得分:3)

而不是在map上使用匿名函数,你应该使用箭头函数(它们自动绑定到当前上下文):

var imageList = imagePreviewUrls.map((value, index) => {
        return (
          <figure className="ma0 relative flex items-center justify-center">
              <button onClick={this._removePhoto} className="button-reset pointer dim bn bg-black h2 w2 br-100 white flex items-center justify-center absolute absolute--fill-l center"><i className="material-icons f5">close</i>
              </button>
              <img key={index} src={value} className="h3 w3" />
          </figure>
          )
    })
  

使用箭头功能时,this会自动引用您在相关环境中的当前this