Search code examples
reactjsnext.jsuse-statecloudinary

Why is the state not updating when post method is done?


I have a form with two inputs. One for just a text and another for image I select from my local PC.

I want to upload the image to cloudinary and use the text URL to be saved in the uploadedImageURL state and then I use that state to update the form state. I want upload the form state later in MongoDB.

The problem I'm facing with this code is that when I start the server and select the image and type the text and click the Add button, it doesn't change the uploadedImageURL and the form state and in console.log, it returns just an empty string.

But, after clicking the Add button a few times with the same inputs. The states finally updates producing the expected result.

I also noticed that after I change the inputs and click Add, it doesn't change the state and sends the previously selected image to cloudinary.

How can I solve this issue?

I want this code to work the first time I click the Add button with the inputs I selected.

export default function Home() {
  const [form, setForm] = useState({ title: "", classImageURL: "" });
  const [modalShown, setModalShown] = useState(false);
  const [imageSelected, setImageSelected] = useState("");
  const [uploadedImageURL, setUploadedImageURL] = useState("");
  const [classTitle, setClassTitle] = useState("");

  const handleSubmit = async (e) => {
    e.preventDefault();
    setModalShown(false);
    await uploadImage()
    setForm({title: classTitle, classImageURL: uploadedImageURL})
    console.log(form)

    // await uploadImage().then(() => {
    //  setForm({ title: classTitle, classImageURL: uploadedImageURL });
    //  console.log("form:", form);
    // });
  };

  const uploadImage = async () => {
    const formData = new FormData();
    formData.append("file", imageSelected);
    formData.append("upload_preset", "cloudinary_default");

    await Axios.post(
      "https://api.cloudinary.com/v1_1/<userid>/image/upload",
      formData
    ).then((response) => {
      setUploadedImageURL(response.data.url);
      console.log(`uploaded image url of cloudinary:`, uploadedImageURL);
    });

  };

  return (
    <div>
      <AddButton modalShown={modalShown} setModalShown={setModalShown} />
      {/* Modal */}
      <div>
        <div
          className={`${
            modalShown ? "" : "hidden"
          } absolute top-1/2 left-1/2 -translate-x-1/2 translate-y-3/4`}
        >
          <form onSubmit={handleSubmit}>
            <div>
              <label>Class Name</label>
              <input
                type="text"
                required
                placeholder="Class Name"
                name="title"
                onChange={(e) => setClassTitle(e.target.value)}
              />
            </div>
            <div>
              <label>Image</label>
              <input
                type="file"
                required
                placeholder="Image"
                name="classImageURL"
                onChange={(e) => setImageSelected(e.target.files[0])}
              />
            </div>
            <div>
              <button onClick={() => setModalShown(false)}>Cancel</button>
              <button onClick={() => console.log("Submit Button Clicked")} type="submit">
                Add
              </button>
            </div>
          </form>
        </div>
      </div>
    </div>
  );
}


Solution

  • States are updated asynchronously so doing console.log after setState or invoking a method which use state just after that won't reflect the new state.

    Use the below code which updates following in your code

    1. Return the URL from uploadImage so that it can be used in handleSubmit method.
    2. Use useEffect to show the form to log on console.

       const handleSubmit = async (e) => {
       e.preventDefault();
       setModalShown(false);
       const imageUrl = await uploadImage();
       setForm({title: classTitle, classImageURL: imageUrl });
    };
        
    const uploadImage = async () => {
        const formData = new FormData();
        formData.append("file", imageSelected);
        formData.append("upload_preset", "cloudinary_default");
        
        const response = await Axios.post(
          "https://api.cloudinary.com/v1_1/<userid>/image/upload",
           formData
         );
        
         setUploadedImageURL(response.data.url);
        
         return response.data.url;
     };
    
     useEffect(() => { 
       console.log(form)
     }, [form]);