5

I would like to upload multiple images first by previewing them then submitting to send them off. I have encountered this: TypeError: Cannot read property 'files' of null. It also only lets me upload just one image.

  • I have created files: [] as means to mount the images for review before submitting.
  • Tried creating an interface files: File[] = file then declaring it in state but get a different error that file does not exist on type {}
import * as React from "react"

class ImageUpload extends React.Component {
  state: {
    files: []
  }

  fileSelectedHandler = (file: any) => {
    let addedFiles = this.state.files.concat(file)
    this.setState({ files: addedFiles })
    console.log("upload file " + file.name)
  }

  render() {
    return (
      < form >
        <div>
          <h2>Upload images</h2>
        </div>
        <h3>Images</h3>
        <input type="file" onChange={this.fileSelectedHandler} />
      </form>
    )
  }
}

export default ImageUpload

I expect it to allow me to select multiple images and store them in the array before sending them off as a batch. Is this even the right way to do it? Any feedback is greatly appreciated.

5 Answers 5

8
  1. In order to fix the error TypeError: Cannot read property 'files' of null. you need to change the state declaration
state = {
   files: []
}
  1. If you want to have the opportunity to select multiple files you can use multiple option
<input type="file" multiple onChange={this.fileSelectedHandler} />

Or if you want to select image one by one, your implementation should work, just fix the state declaration and use e.target.files to get selected file

class ImageUpload extends React.Component {
  state = {
    files: []
  }

  fileSelectedHandler = (e) => {
    this.setState({ files: [...this.state.files, ...e.target.files] })
  }

  render() {
    return (
      <form>
        <div><h2>Upload images</h2></div>
        <h3>Images</h3>
        <input type="file" multiple onChange={this.fileSelectedHandler} />
      </form>
    )
  }
}


ReactDOM.render(<ImageUpload />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

1
  • 1
    Thank you,this works quite well. Would also like to point out the error I was getting was because I used an : instead of = when declaring my state. Woops.
    – Prozak
    Commented Apr 24, 2019 at 14:24
3

Updated Answer with Typescript as a sperate componnent

import React, { useState, useEffect } from "react";

const UploadAndDisplayImage = () => {
  const [images, setImages] = useState([] as any);
  const [imageURLS, setImageURLs] = useState([]);
  useEffect(() => {
    if (images.length < 1) return;
    const newImageUrls: any = [];
    images.forEach((image:any) => newImageUrls.push(URL.createObjectURL(image)));
    setImageURLs(newImageUrls);
  }, [images]);

  function onImageChange(e: any) {
    setImages([...e.target.files]);
  }

  return (
    <>
      <input type="file" multiple accept="image/*" onChange={onImageChange} />
      {imageURLS.map((imageSrc) => (
        <img src={imageSrc} alt="not fount" width={"250px"} />
      ))}
    </>
  );
};

export default UploadAndDisplayImage;
0

Handle Form Initial data:

const initialProducts = {
name: "",   
featured_image: "", //for single image
slider_images: [], // (array of strings)   
};

useState

const [products, setProducts] = useState(initialProducts);

Function: Handle Input Fields

const handleInput = (e) => {
let updateValues = { ...products };
updateValues[e.target.name] = e.target.value;
setProducts(updateValues);
console.log("Update input values", updateValues);
};

Function: Handle Multiple Images

 const handleSliderImages = (e) => {
  if (e.target.files) {
  setProducts({ ...products, slider_images: [...e.target.files] });
  }
  console.log("Update slider images", products);
};

FormGroup for Input Fields

<FormGroup className="my-3">
  <Label for="name">Name</Label>
   <Field
    name="name"
    id="name"
    onChange={handleInput}
    value={products.name}
   />
</FormGroup>

FormGroup for single Image

 <FormGroup className="my-3">
  <Label for="featured_image">Featured Image</Label>
  <CustomInput
   type="file"
   id="featured_image"
   name="featured_image"
   onChange={handleInput}
   value={products.featured_image}                  
  />               
 </FormGroup>

FormGroup for Multiple Images

<FormGroup className="my-3">
 <Label for="slider_images">Slider Images</Label>
 <CustomInput
 multiple
 type="file"
 id="slider_images"
 name="slider_images"
 onChange={handleSliderImages}
 />                
</FormGroup>
0

You can solve this problem by multiple input fields. You are able to track the input field used to upload the file using their unique "ID". Please take a look to the code that I shared.

Input field

onChange handler

file handler based on id

0

create states(the images state u can use in api if you wanna send those images to api

handle images with createProductImageChange

add input with multiple attribute and use imagesPreviewState for whoweing on the screen

It is simple You should follow the steps which i wrote and i hope this will help

1
  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Oct 3, 2022 at 12:10

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.