0

I have a code snippet here that lets me validate the image upload as well as preview it:

const validateAndPreview = (event) => {
      if(event.target.files.length < 1){
         return;
      }

      const file = event.target.files[0]

      const validExtensions = ["png", "jpg", "jpeg"];
      const fileExtension = file.type.split("/")[1]
            
      if(validExtensions.includes(fileExtension)){
         setError({ error: false, message: "" });
         let fileReader = new FileReader();

         fileReader.onload = (e) => {
            setImagePreview(e.target.result);
         }

         fileReader.readAsDataURL(file)
      }else{
         setError({ error: true, message: "File extension is invalid" });
         setImagePreview("");
      }
   }

Do I need to release the fileReader object and remove the load event handler to avoid memory leaks or all of this is being done automatically with fileReader?

I assumed that it might be the way to go, but I'm not sure if it makes sense.

fileReader.onload = (e) => {
    setImagePreview(e.target.result);
    reader.onload = null;
    reader = null;
}

(edited) Implementation with createObjectURL

   const validateAndPreview = (event) => {
      if(event.target.files.length < 1){
         return;
      }

      const file = event.target.files[0]

      const validExtensions = ["png", "jpg", "jpeg"];
      const fileExtension = file.type.split("/")[1]
            
      if(validExtensions.includes(fileExtension)){
         setError({ error: false, message: "" });
         const fileURL = URL.createObjectURL(file);

         setImagePreview(fileURL);
      }else{
         setError({ error: true, message: "File extension is invalid" });
         setImagePreview("");
      }
   }

   useEffect(() => {
      return () => {
         if (imagePreview) {
            console.log("UNMOUNT")
            console.log(imagePreview)
            URL.revokeObjectURL(imagePreview);
            console.log("AFTER:", imagePreview)

         }
      };
   }, [imagePreview])
Jul
  • 9
  • 2
  • 1
    BTW, rather than using `FileReader` you could just load `file` into `URL.createObjectURL()` and pass that into `document.createElement("img")`'s `.src` property and checking if either its `error` or `load` events fire (though you will need to call `URL.revokeObjectURL` at the end, but it's still less overall work than faffing around with `FileReader`. – Dai Jul 12 '23 at 12:44
  • `fileReader.readAsDataURL(file)` <-- Please, _please_ don't use `readAsDataURL`: there is **no good reason** for using the horribly slow and inefficient (and _browser-UI-thread-blocking_) Base64 `data:` URIs. You are working with binary files, so you should use binary techniques. – Dai Jul 12 '23 at 12:46
  • 1
    I've added code with createObjectURL. Would it be correct? Aren't there possible memory leaks? – Jul Jul 12 '23 at 19:51
  • Please show your definition of `setImagePreview` – Dai Jul 12 '23 at 19:52
  • I'm not a React.js user and I'll admit I'm not entirely sure how `useEffect` works in this case - but if this were _vanilla JS_ I'd use a `try/finally` to ensure the `URL.revokeObjectURL` call _always_ happens. – Dai Jul 12 '23 at 19:53
  • const [imagePreview, setImagePreview] = useState(""); this is the state, i use imagePreview as a src of image. useEffect is being called whenever the component is unmounted (when the user uploads the new picture it is being called as well), so I assume that it must work. however in devtools (performance tab) it shows that heap usage is growing, as well as nodes and listeners. but i never really used performance tab, so maybe I'm getting it wrong and I should read up on how it works. – Jul Jul 12 '23 at 20:03

1 Answers1

0

No you do not particularly have to remove the handlers and all, but it dose not hurt. Javascript have garbage collector that keeps track of what you have any references to. if you no longer has a references to it then it will be garbage collected eventually.

but as other points out. it's better to use URL.createObjectURL instead of using the filereader to obtain a object url instead of a long base64 url.

but then the question becomes: Do i need to revoke it? then i would say it depends. read this https://stackoverflow.com/a/49346614/1008999

if it's coming from a file input then you don't particularly have to worry about not revoking object urls... cuz they are only small pointers to where the file is located on the disc


On a side note i would not also bother with doing client side validation with javascript. instead use the accept attribute to limit the file selection to only images so they can't pick a wrong file. there are more image format that works too. so why limit it? allow image/* mime types. if they are of any unknown format you don't want to handle on the backend then you could always convert those images to png with OffscreenCanvas and convert the image to png if you like.

Endless
  • 34,080
  • 13
  • 108
  • 131