“React formData with multiple files and fields not updating on PUT request”

I’m building an admin panel in React where admins can edit guides. The edit form includes fields for:

Title (text input)
Content (an array of paragraphs)
Images (file uploads or URLs)
I use a FormData object to send this data to the server via a PUT request. Everything works fine when testing with Postman, and the server updates the guide as expected.

However, in the React app:

The FormData prints correctly to the console on the client-side, showing all appended fields (title, content, and images).
No errors are thrown when sending the request.
On the server-side, the fields (req.body and req.files) are received as undefined.
The client receives a response that includes the original, unmodified guide object, as if no changes were applied.

import React, { useState } from "react"
import axios from "axios"
import { EDIT_GUIDE_URL } from "../constants/endPoint"

export const EditGuideWin = (props) => {
  const [title, setTitle] = useState(props.title || "")
  const [content, setContent] = useState(props.content || [])
  const [images, setImages] = useState(props.images || [])
  const [newImage, setNewImage] = useState("")
  const [newParagraph, setNewParagraph] = useState("")
  const [files, setFiles] = useState([])

  const handleAddParagraph = () => {
    if (newParagraph.trim()) {
      setContent([...content, newParagraph.trim()])
      setNewParagraph("")
    }
  }

  const handleRemoveParagraph = (index) => {
    setContent(content.filter((_, i) => i !== index))
  }

  const handleRemoveImage = (index) => {
    setImages(images.filter((_, i) => i !== index))
  }

  const handleFileUpload = (e) => {
    const selectedFiles = Array.from(e.target.files)
    setFiles([...files, ...selectedFiles])
  }

  const handleAddImage = () => {
    if (newImage.trim()) {
      setImages([...images, newImage.trim()])
      setNewImage("")
    }
  }

  const onSubmit = async (e) => {
    e.preventDefault()
    const formData = new FormData()

    formData.append("title", title)
    content.forEach((para) => {
      formData.append("content[]", para)
    })

    if (files.length > 0) {
      files.forEach((file) => {
        formData.append("images", file)
      })
    }

    try {
      const response = await axios.put(
        `${EDIT_GUIDE_URL}/${props._id}`,
        formData,
        {
          headers: {
            "Content-Type": "multipart/form-data",
          },
        }
      )
      console.log("Response data:", response.data)
      props.onClose()
    } catch (err) {
      console.error("Error during guide update:", err)
      alert("Error while updating guide")
    }
  }

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
      <div className="w-[80%] max-w-md overflow-x-auto rounded-lg bg-white p-4 shadow-lg">
        <h2 className="mb-4 text-xl font-bold">Edit Guide</h2>
        <form onSubmit={onSubmit} method="put" className="flex flex-col gap-2">
          <label htmlFor="title">Title:</label>
          <input
            type="text"
            id="title"
            value={title}
            onChange={(e) => setTitle(e.target.value)}
            className="w-full rounded-md border border-black p-2"
          />

          <label htmlFor="content">Content:</label>
          <div className="flex flex-col gap-2">
            {content.map((paragraph, index) => (
              <div key={index} className="flex items-center gap-2">
                <textarea
                  value={paragraph}
                  onChange={(e) => {
                    const updatedContent = [...content]
                    updatedContent[index] = e.target.value
                    setContent(updatedContent)
                  }}
                  className="flex-1 rounded-md border border-black p-2"
                />
                <button
                  type="button"
                  onClick={() => handleRemoveParagraph(index)}
                  className="redBtn"
                >
                  Remove
                </button>
              </div>
            ))}
          </div>
          <div className="flex items-center gap-2">
            <textarea
              placeholder="Add new paragraph"
              value={newParagraph}
              onChange={(e) => setNewParagraph(e.target.value)}
              className="flex-1 rounded-md border border-black p-2"
            />
            <button
              type="button"
              onClick={handleAddParagraph}
              className="greenBtn"
            >
              Add
            </button>
          </div>

          <label>Images:</label>
          <div className="flex flex-col gap-2">
            {images.map((img, index) => (
              <div key={index} className="flex items-center gap-2">
                <img
                  src={img}
                  alt={`Guide ${index}`}
                  className="h-16 w-16 object-cover"
                />
                <button
                  type="button"
                  onClick={() => handleRemoveImage(index)}
                  className="redBtn"
                >
                  Remove
                </button>
              </div>
            ))}
          </div>

          <div className="flex items-center gap-2">
            <input
              type="text"
              placeholder="Image URL"
              value={newImage}
              onChange={(e) => setNewImage(e.target.value)}
              className="flex-1 rounded-md border border-black p-2"
            />
            <button type="button" onClick={handleAddImage} className="greenBtn">
              Add
            </button>
          </div>

          <label>Upload Image:</label>
          <input
            type="file"
            multiple
            onChange={handleFileUpload}
            className="mb-4"
          />

          <div className="flex justify-end gap-2">
            <button type="button" onClick={props.onClose} className="redBtn">
              Cancel
            </button>
            <button type="submit" className="greenBtn">
              Save
            </button>
          </div>
        </form>
      </div>
    </div>
  )
}

I expected the server to correctly receive and process the updated guide details from the formData.

Steps I tried:

Checked that formData is populated correctly with append() calls for each field (title, content, and images).
Verified that Content-Type is automatically set to multipart/form-data in the headers by Axios.
Logged the request payload on the server to debug (it shows undefined for all fields).
Tested the API directly with Postman, where it works as expected (the server updates the guide correctly).
Still, in the React app, the PUT request seems to send undefined fields to the server, and the server returns the unchanged guide objec