2

I'm making a file input to select multiple images that can be pre-visualized.

To handle multiple images I put them in an array of the component's state, and map the pre-visualized images.

But when I select the images from the input and set the state with this.setState({imgArray: newArray}), this.state.array.map(image=><img src={image}/>) doesn't re-render the selected images.

Code:

export default class posts extends Component { state = { fotos: ["/iconos/img.svg"] // If there are not img selected, it renders one image icon } onUploadVarious = e => { let newArray = [] Object.values(e.target.files).map(file => { let nuevo = new FileReader() nuevo.onload = event=> newArray.push(event.target.result) nuevo.readAsDataURL(file)} ) this.setState({fotos: newArray}) // the state is set correctly } } 

Render:

<div className=" border rounded" style={{"height":"30%", "overflow":"auto"}}> {this.state.fotos.map(foto => <img src={foto||"/iconos/img.svg"} id="arch-preview"/>)} // it doesn't re-render when fotos state is changed </div> // input <div className="mt-auto"> <input multiple="multiple" type="file" onChange={this.onUploadVarious} className="form-control-file" name="file" /> </div> 
1
  • readAsDataURL is asynchronous, so you need to call the setState after that data is read, which should be in the onload function I believe. Commented May 18, 2020 at 4:21

1 Answer 1

5

FileReader reads file content asynchronously.

Due to this asynchronous nature, state is being set i.e. this.setState({fotos: newArray}) before data urls are set in newArray i.e. newArray.push(event.target.result).

And that's the reason your selected files aren't showing up.

To fix it, you can use create Promise which gets resolved after load event of each file. And use Promise.all which would be resolved after each Promise has resolved and then use setState:

readAsDataURL = (file) => { return new Promise((resolve, reject) => { const fr = new FileReader() fr.onerror = reject fr.onload = function () { resolve(fr.result) } fr.readAsDataURL(file) }) } onUploadVarious = (e) => { Promise.all(Array.from(e.target.files).map(this.readAsDataURL)) .then((urls) => { this.setState({ fotos: urls }) }) .catch((error) => { console.error(error) }) } 

This has some good examples of Promises and their executing orders. Also check this about using FileReader with Promise.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.