Omar
Omar

Reputation: 3411

storing uploaded images in react state with file reader

How come reader.onloadend does not execute? What i'm trying to do is store the src of the images uploaded and render a preview when I map through images array in the render method. What needs to change for file reader methods to actually execute?

import React, { Component } from 'react';
import { Button, Input } from 'semantic-ui-react';
class Uploadimage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      file: '',
      images:[],
    };
  }

setImages = (e) => {
  e.preventDefault();
  let { images } = this.state;
  const imageFiles = document.getElementById("image");
  const filesLength = imageFiles.files.length;
  const temp = null;
  let reader = new FileReader();

  reader.onloadend = () => {
  for(var i = 0; i < filesLength; i++) {
      let file = e.target.files[i];
      temp.push(reader.readAsDataURL(file));
    }
    this.setState({
      images:temp
    })
  }
}

  render() {
    let { images } = this.state;
    return (
      <div>
        <form onSubmit={this._handleSubmit}>
          <Input id="image" type="file" multiple defaultValue='no file chosen' onChange={this.setImages} />
          <Button icon='upload' type="submit" onClick={this._handleSubmit}>Upload Image</Button>
        </form>
        {images.map((item,index) => <img src={item} />)}
      </div>
    )
  }

}

export default Uploadimage;

Upvotes: 1

Views: 8351

Answers (2)

Varinder
Varinder

Reputation: 2664

I believe you'll have to move for loop logic out of reader.onloadend method

Until now I was under the impression that when using react, any form field can be turned into a controlled component, however fileUpload appears to be an exception

See updated code with comments below, I haven't tried this sorry:

class Uploadimage extends Component {
    constructor(props) {
        super(props);
        this.state = {
            file: '',
            images:[],
        };

        this.fileReader = new FileReader();
    }

    setImages = (e) => {
        e.preventDefault();
        let self = this; // unsure if this is needed
        self.setState({ images: [] }); // empty out current images array
        const imageFiles = e.target.files; // document.getElementById("image"); // You may want to avoid querying the dom yourself, try and rely on react as much as possible
        const filesLength = imageFiles.length; // imageFiles.files.length;
        // const temp = null;

        for(var i = 0; i < filesLength; i++) {
            let reader = new FileReader();
            let file = imageFiles[i];

            reader.onloadend = () => {
                self.setState({ images: self.state.images.concat(reader.result); });
            }

            reader.readAsDataURL(file);
        }
    }

    render() {
        let { images } = this.state;
        return (
            <div>
                <form onSubmit={this._handleSubmit}>
                    <Input id="image" type="file" multiple defaultValue='no file chosen' onChange={this.setImages} />
                    <Button icon='upload' type="submit" onClick={this._handleSubmit}>Upload Image</Button>
                </form>
                {images.map((item,index) => <img src={item} />)}
            </div>
        )
    }
}

Upvotes: 2

Tomasz Bubała
Tomasz Bubała

Reputation: 2153

There are couple of wrong things with your code:

  1. reader.readAsDataURL(file) is inside onloadend callback - it should be outside, so the reader can read files and then call onloadend after.
  2. temp is null and you try to use push method. Set it to empty array.
  3. You don't really need document.getElementById in React - use refs

Upvotes: 0

Related Questions