Improve performance when loading images with reactjs

0

I'm starting with react, and as an exercise I want to make a drag & amp image charger. Drop using components.

The problem is that when loading an image the RAM consumption of the navigator rises, but when dragging images again to the component, the memory consumption rises even though previously loaded images have already been deleted.

class ImagesLoader extends React.Component{
  constructor(props){
    super(props)
    this.state = {images: []}
    this.AddImages = this.AddImages.bind(this)
    this.handleUpdateImages = this.handleUpdateImages.bind(this)
  }

  AddImages(e){
    e.preventDefault()
    let images= []
    let files = e.dataTransfer.files
    for(let i = 0; i < files.length;i++){
      let file = files[i]
      if(file.type.split('/')[0] === "image"){
        let name = file.name.substring(0,file.name.lastIndexOf("."))
        let newImage = {
          title: name,
          file: file,
          category: 1
        }
        images.push(newImage)
      }
    }
    this.setState({
      images: images
    })
  }

  handleUpdateImages(upImages){
    this.setState({images: upImages})
  }

  render(){
    return(
      <div
        className="ImagesLoader">
        <Droparea onDropFiles={this.AddImages}/>
        <Previews
          images={this.state.images}
          onUpdateImages={this.handleUpdateImages}
        />
      </div>
    )
  }
}

class Droparea extends React.Component{
  constructor(props){
    super(props)
    this.handleDragOver = this.handleDragOver.bind(this)
    this.handleDragLeave = this.handleDragLeave.bind(this)
  }

  handleDragOver(e){
    e.preventDefault()
    let target = e.target
    target.classList.add('over')
  }

  handleDragLeave(e){
    e.preventDefault()
    let target = e.target
    target.classList.remove('over')
  }

  render(){
    return(
      <div
        className="Droparea"
        onDragOver={this.handleDragOver}
        onDragLeave={this.handleDragLeave}
        onDrop={this.props.onDropFiles}>
        <span className="Droparea-text"> Drag your elements Here. </span>
      </div>
    )
  }
}

class Previews extends React.Component{
  constructor(props){
    super(props)
    this.handleChangeItem = this.handleChangeItem.bind(this)
  }

  handleChangeItem(oldItem,prop,newValue){
    let images = this.props.images
    images.map((image) => {
      if(image === oldItem){
        image[prop] = newValue
      }
    })
    this.props.onUpdateImages(images)
  }

  render(){
    let images = this.props.images
    const imageList = images.map(image =>
      <PreviewItem
            key={image.file.name}
            image={image}
            onChangeItem={this.handleChangeItem}/>
    )

    return(
      <ul className="Previews">
        {imageList}
      </ul>
    )
  }
}

class PreviewItem extends React.Component{
  constructor(props){
    super(props)
    this.handleChangeTitle = this.handleChangeTitle.bind(this)
    this.handleChangeCategory = this.handleChangeCategory.bind(this)
  }

  handleChangeTitle(value){
    this.props.onChangeItem(this.props.image,'title',value)
  }

  handleChangeCategory(value){
    this.props.onChangeItem(this.props.image,'category',value)
  }

  render(){
    return(
      <li className="Preview-item">
        <PreviewItemImg image={this.props.image}/>
        <PreviewItemForm
          image={this.props.image}
          onChangeTitle={this.handleChangeTitle}
          onChangeCategory={this.handleChangeCategory}
          />
      </li>
    )
  }
}

class PreviewItemImg extends React.Component{
  constructor(props){
    super(props)
    this.state = {image: this.props.image, src: false}
    this.getImage().then(src => {
      this.setState({src: src.src})
    })
  }

  getImage(){
    return new Promise((resolve,reject) => {
      let image = this.props.image.file
      let fileReader = new FileReader()
      fileReader.onload = function(){
        let base64 = this.result
        let img = new Image()
        img.onload = function(){
          resolve(this)
        }
        img.src = base64
      }
      fileReader.readAsDataURL(image)
    })
  }

  render(){
    if(!this.state.src){
      return null
    }
    return(
      <div className="Preview-item-img">
        <img className="Item-img" src={this.state.src}/>
      </div>
    )
  }
}

class PreviewItemForm extends React.Component{
  constructor(props){
    super(props)
    let title = (this.props.image.file.name === this.props.image.name)
             ? this.props.image.file.name
             : this.props.image.title
    this.state = {
      title: title,
      category: this.props.image.category
    }
    this.handleChangeTitle = this.handleChangeTitle.bind(this)
    this.handleChangeCategory = this.handleChangeCategory.bind(this)
  }

  handleChangeTitle(e){
    let value = e.target.value
    this.setState({title: value})
    this.props.onChangeTitle(value)
  }

  handleChangeCategory(e){
    let value = e.target.value
    this.setState({category: value})
    this.props.onChangeCategory(value)
  }


  render(){
    const categories = [
      {value: 1, name: 'Animals'},
      {value: 2, name: 'Cgi'},
      {value: 3, name: 'Landscapes'},
      {value: 4, name: 'Women'}
    ]
    return(
      <form className="Preview-item-form">
        <input type="text"
               placeholder="Insert title"
               onChange={this.handleChangeTitle}
               value={this.state.title}
        />
        <select value={this.state.category}
                onChange={this.handleChangeCategory}>
          {
            categories.map((c) =>
              <option
                key={c.value}
                value={c.value}>
                {c.name}
              </option>
            )
          }
        </select>
      </form>
    )
  }
}

ReactDOM.render(
  <ImagesLoader />,
  document.getElementById('root')
);
.ImagesLoader {
  font-family: Ubuntu;
  width: 100%;
}
.ImagesLoader .Droparea {
  display: flex;
  flex-flow: row wrap;
  justify-content: center;
  align-items: center;
  min-height: 300px;
  height: 30%;
  width: 100%;
  background-color: #1a1a1a;
  color: #808080;
  transition: all 0.2s ease-in;
}
.ImagesLoader .Droparea.over {
  color: #1a1a1a;
  background-color: #ff6347;
}
.ImagesLoader .Previews {
  padding: 0;
  margin: 0;
  min-height: 600px;
  width: 100%;
}
.ImagesLoader .Previews .Preview-item {
  list-style-type: none;
  margin: 0;
  padding: 0;
  border: dashed 1px #ff6347;
}
.Preview-item-img {
  position: relative;
}
.Preview-item-img img {
  position: relative;
  max-width: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

    <div id='root'>

    </div>

What do you think is the cause of the problem regarding RAM?

NOTE:

If you observe any bad practice in my code please let me know,

    
asked by Edwin V 28.04.2017 в 18:10
source

1 answer

0

I do not see any type of memory loss ( memory leak ) I have used the tool in performance chrome and the behavior is normal, this is the result:

As you can see I have tried with different images and without getting lost memory result the peaks rise when I do the drop but the memory returns to stabilize which you use around 52MB - 57MB

And in the code I do not see signs of memory leak either. Please check that your extensions are not affecting the performance on your page (chrome, firefox). It would be best if you try your code in incognito mode if memory issues are the case.

    
answered by 30.07.2017 в 14:40