reactjsfetchsetstate

React setState from fetch and send it to another fetch


I have a function that calls 2 functions, one uploads an image and stores it in some directory, and the other send product related data to edit/store in the database:

  const uploadImage = async () => {
    let formData = new FormData();
    formData.append("image", product.image);

    const res = await fetch("http://localhost:8000/api/upload", {
      method: "POST",
      body: formData,
    });
    const data = await res.json();
    setProduct({...product,path: data.data.path});
  };

  const saveProduct = async () => {

    if (product.id) {
      await fetch("http://localhost:8000/api/edit-product", {
        method: "PUT",
        body: JSON.stringify(product),
        headers: { "Content-Type": "application/json" },
      });

    } else {
      fetch("http://localhost:8000/api/new-product", {
        method: "POST",
        body: JSON.stringify(product),
        headers: { "Content-Type": "application/json" },
      })
    }

  };

  const saveProductMain = () => {
    uploadImage();
    saveProduct();

  };

when saveProductMain is called, it will call uploadImage which gets a response for the uploaded image path, and then sets the state product.path, now i want to send that path to /new-product so i can recieve the path in the route method and then store it in the database, but when i console.log the state product.path in saveProduct function, it will return the value only the second time the function is called, the first time it returns null, so product.path is null when its sent in the fetch body, how can i solve that issue ?


Solution

  • State updates don't take effect until the next render, this is why it only works on the second time. product in state can never be updated by the time you call the second function.

    One simple alternative is to have the first function return the product and use that value directly in the second. You may be able to remove product from state altogether, but if you still need it in state you can leave it.

    Example:

      const uploadImage = async () => {
        let formData = new FormData();
        formData.append("image", product.image);
    
        const res = await fetch("http://localhost:8000/api/upload", {
          method: "POST",
          body: formData,
        });
        const data = await res.json();
    
        // You may be able to delete this line if not needed elseware in your code.
        setProduct({...product,path: data.data.path});
    
        return {...product,path: data.data.path};
      };
    
      const saveProduct = async (product) => {
    
        if (product.id) {
          await fetch("http://localhost:8000/api/edit-product", {
            method: "PUT",
            body: JSON.stringify(product),
            headers: { "Content-Type": "application/json" },
          });
    
        } else {
          fetch("http://localhost:8000/api/new-product", {
            method: "POST",
            body: JSON.stringify(product),
            headers: { "Content-Type": "application/json" },
          })
        }
    
      };
    
      const saveProductMain = async () => {
        const product = await uploadImage();
        saveProduct(product);
      };
    

    Since uploadImage is async, you will need to make your save function async as well so that you can await the uploadImage call.