简体   繁体   中英

Why must I pass a function to the setter function of 'useState' in React?

I'm handling file uploads using react-dropzone , which calls the following function with a list of files:

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

    const onDrop = useCallback(async acceptedFiles => {
        uploadFiles(acceptedFiles)
    }, [])

I then upload the files using fetch:

    const uploadFiles = async acceptedFiles => {
        acceptedFiles.forEach(async (file) => {
            setFiles(array => [...array, fileState])

            await fetch('/upload', {
                method: 'POST',
                body: data
            }).then(res => {
                // etc
            })
        })
    }

If I replace the setFiles call with just setFiles([...files, fileState]) (not in a function) it breaks, but why is this?

I believe I was able to reproduce the issue you're having. https://codesandbox.io/s/elegant-bouman-9lb5k?file=/src/App.js

Here is the relevant code:

  const [files, setFiles] = useState(["file1", "file2"]);

  const uploadFiles = async (acceptedFiles) => {
    acceptedFiles.forEach(async (file) => {
      //setFiles([...files, file]);
      setFiles(array => [...array, file])

      await fetch("https://httpstat.us/200");
    });
  };

  const testFunc = async (acceptedFiles) => {
    uploadFiles(["file3", "file4"]);
  };

If behaves as expected when called like the above, but if you swap the setFiles calls, only file4 will be appended.

The issue appears to be related to React batching setState calls as they are made in close succession. This post provides the best answer I could find.

So it appears that when you call your setFiles with the callback, React does not batch those requests and thus executes them one after the other.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM