Berserk Guts
Berserk Guts

Reputation: 85

I need to execute a function that will update the state right after using setState hook, but state inside function is empty?

I am trying to upload files and show progress while the files are uploading, what I am doing is when the user selects the files from the input I have an onChange listener that will take the files and build a new state with objects with 2 fields file and progress like this:

    const [files,setFiles] = useState([]);

    const handleFileUpload = async (e) => {
        const fileList = Array.from(e.target.files);
        const filesStructure = fileList.map((file: any) => ({
          progress: 0,
          file,
        }));

       const clonedFiles = [...files, ...filesStructure];
       setFiles(clonedFiles);
  
        filesStructure.forEach((fileItem) => {
          uploadFile(fileItem.file, post.id);
        });
      }
    e.target.value = "";
  };

now in my uploadFile I want to update the progress for each file but if I check there the state is an empty array


    const uploadFile = async (file, postId: string) => {
        console.log("files are:", files);
        setIsLoading(true);
        const formData = new FormData();
        formData.append("file", file);
        formData.append("post_id", postId);
        try {
          const onprogress = (event) => {
               clonedFileItem.progress = Math.round((event.loaded * 100) / event.total);
               const clonedFiles = [...files];
               clonedFiles[index] = clonedFileItem;
               setFiles(clonedFiles);
          };

      const res = await uploadFileService(formData, onprogress);

    } catch (error) {
      console.log(error.message);
    } finally {
      setIsLoading(false);
    }
  };

I am not very sure how should I approach this problem? I need to have access to the updated state inside uploadFile and inside the onprogress function.

Upvotes: 3

Views: 44

Answers (2)

Alpe89
Alpe89

Reputation: 299

As a general rule, with React every time there is a so called "side effect", you should use a useEffect hook.

What I suggest to you is using a useEffect hooks that executes its function when the file variable (the one that you defined with your useState hook) changes. Inside this effect you can run whatever logic you want in order to change the ui accordingly

Upvotes: 0

phry
phry

Reputation: 44078

Generally, you are using a lot of variable references here, that will all at some points run stale.

files will reference the files array at the time uploadFile was defined before it was called.

You can get around that in many cases by just using the callback notation.

      const onprogress = (event) => {
           setFiles(oldFiles => {
             clonedFileItem.progress = Math.round((event.loaded * 100) / event.total);
             const clonedFiles = [...oldFiles];
             clonedFiles[index] = clonedFileItem;
             return clonedFiles
           }
      };

Upvotes: 3

Related Questions