Search code examples
pythonreactjsflaskaxios

Axios post call works but no other code is run, state not updated


The frontend has a function that handles a file upload and send the file to the backend to be processed. The backend then returns a json that should be used to update my React state. I set up a test call with axios.get() that works fine, where the backend passes in a string that is displayed on the webpage. console.log() also works fine in this test function. When it comes to my uploadDoc function though, no console.log() s get run and state isn't updated. Here's my code:

App.js

const documentUploadHandler = ({files}) => {
    const [file] = files;
    uploadDoc(file);
  };

  const uploadDoc = async (document) => {
    console.log(doc_type)
    document.preventDefault();
    let formData = new FormData();
    formData.append('document', document);

    try {
      const response = await axios.post('https://127.0.0.1:8000/api/upload', formData, {
        headers: {
          'Content-Type': 'multipart/form-data'
        }
      });

      setDocType([response.data.classification]);
      console.log("hello")
      console.log(response.data);

    } catch (error) {
      console.log('Error uploading file:', error);
    }
  };

Server.py

@app.route('/api/upload', methods=['POST', 'GET'])
@cross_origin(supports_credentials=True)
def upload_file():
    global class_result
    if request.method == 'POST':
        try:
            file = request.files['document']
            print(f"Uploading document {file.filename}")
            filename = secure_filename(file.filename)
            file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
            file.save(file_path)
            class_result = classify_document("model5", file_path)
            return jsonify({"status": "get_success", "classification": class_result})

        except Exception as e:
            print(f"Couldn't upload document: {e}")
            return jsonify({"status": "failed", "error": str(e)})
    return jsonify({"status": "post_success", "classification": class_result})

I've tried making the API call in Postman, which strangely only gives me the success status when I make a GET request, not a POST request. I'm not sure if this is relevant to the problem I'm having. I checked with dev tools in the Network tab, and it gives me status code 200 and it says that my request method is POST, so it seems to be using the right request type on the web.

I also tried updating the state in a separate function with axios.get() . This works in the sense that the new state is displayed, but only after I refresh the page. However, I need the state to be updated as soon as the backend has finished processing the file, which is why I'm trying to update state after my axios.post() call.


Solution

  • In short the axios.post create a promise not an actual value. The Promise is something that needs to be handled properly when the promise is fulfilled (since its an asynchronous call). Usually with .then((r)=>{setState(r)});

    So for your case this should suffice:

    axios.post('https://127.0.0.1:8000/api/upload', formData, {
            headers: {
              'Content-Type': 'multipart/form-data'
            }
          }).then((response)=>{
          setDocType([response.data.classification]);
          console.log("hello")
          console.log(response.data);
          })
          .catch(err => {
            // Handle errors
            console.error(err);
        });
    

    Example component implementation, note the json parse, this only applies if they are actually json results:

    export default function UploadFiles() {
        const [files, setFiles] = useState<File[]>([]);
        const filesRef = useRef(null);
        filesRef.current = files;
        const [backendAnswer, setBackendAnswer] = useState<any>();
    
        const handleFileSelect = useCallback((event: ChangeEvent<HTMLInputElement>) => {
            if (event.target.files.length === 0) setFiles([]);
            else setFiles([...event.target.files]);
        }, []);
    
        const uploadFiles = useCallback(() => {
            if (filesRef.current.length != 1) return;
            let formData = new FormData();
            formData.append('document', filesRef.current[0]);
    
            axios.post('https://127.0.0.1:8000/api/upload', formData, {
                headers: {
                    'Content-Type': 'multipart/form-data'
                }
            }).then((response) => { 
                if (!response.ok) throw new Error("Error in request at " + url);
                else return response.json();
            }).then((jsonParsedResponse)=>{
                console.log(jsonParsedResponse)
                setBackendAnswer(jsonParsedResponse);
            }).catch((error) => { console.error(error) });
        }, []);
    
        return <>
            <div className="flex flex-col">
                <label
                    htmlFor="fileUpload"
                >
                    Select files
                    <input
                        id="fileUpload"
                        name="fileUpload"
                        type="file"
                        className="hidden"
                        onChange={handleFileSelect}
                    />
                </label>
            </div>
            <button onClick={uploadFiles} >Upload</button>
        </>
    }