0

So I'm trying to make a component to select multiple images and display a preview, so then the user can see the selected images and decide to upload it or not.

So here's my code to select images and display the preview :

  const [fileObj, setFileObj] = useState([]);

const loadImageHandler = (event) => {
    setImageUpload(event.target.files);
    setShowProgress(true);

    for (let i = 0; i < event.target.files.length; i++) {
      setFileObj((arr) => [
        ...arr,
        {
          file: event.target.files[i],
          url: URL.createObjectURL(event.target.files[i]),
        },
      ]);
    }
  };

return (
    <div className={classes.container}>
      <div className={classes.imgPreview}>
        {fileObj.length > 2 &&
          fileObj.map((obj, index) => <img id={index} src={obj.url} />)}
      </div>
      <div>
        <label id="articleImg">Select images for your article :</label>
        <input
          name="articleImg"
          type="file"
          onChange={loadImageHandler}
          accept="image/*"
          required={required}
          multiple
        />
      </div>
    </div>
  );

The problem is that when I select my images, the input field indicates that there are 3 images selected for example, but the preview does not display anything. It is when I select the images a second time that the preview shows the new images that have just been selected and the previous ones. How can I update my preview during the first selection?

2 Answers 2

0

That because you are using setState method in the loop. So in next render, actually there is only one data in fileObj array. Because of 'fileObj.length > 2' condition, so the image won't be rendered.

Try change 'loadImageHandler' method to this way.

const loadImageHandler = (event) => {
    setImageUpload(event.target.files);
    setShowProgress(true);
    // First to figure out the result array by using map method
    const result = event.target.files.map(v => ({
       file: v,
       url: URL.createObjectURL(v),
    }));
    
    setFileObj((arr) => [...arr, ...result]);
 };

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

1 Comment

As I said to Anthony, event.target.files is a FileList so we can't use Array's methods like map on it. So I just converted it to get an array like that : const arr = Array.from(event.target.files). Thank you for the explication of the setState in the loop !
0

Ok so there is a problem in your function, this is not a good practice to set the state in a for loop, do it this way:

const loadImageHandler = (event) => {
    setImageUpload(event.target.files);
    setShowProgress(true);

    setFileObj(event.target.files.map(item => ({
        file: item,
        url: URL.createObjectURL(item)
    })))
  };

1 Comment

event.target.files is a FileList so we can't use Array's methods like map on it. So I just converted it to get an array like that : const arr = Array.from(event.target.files). Now it works perfectly thanks you !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.