-1

I'm using cloudinary for users to be able to upload an avatar image with next js and I've set a controller file in the backend that triggers the function to edit profile data with mongoDB and node js. The url of the cloudinary image is supposed to be received by the controller as part of the object called "datosActualizados" when handleSubmit is triggered (the state is called "urlImagen". However, the state is not received correctly the first time but the second one. I though it could be that the put request was getting executed before the state is set, so I tried adding a setTimeOut function to it, but the issue persists. Any help would be greatly appreciated:

 const [urlImagen, setUrlImagen] = useState(auth.imagen)

     try {

            // Eliminar avatar anterior de Cloudinary

            const timestamp = new Date().getTime();
            const signature = generateSHA1(`public_id=${process.env.CLOUDINARY_UPLOAD_PRESET}/${usuarioID}&timestamp=${timestamp}${process.env.CLOUDINARY_API_SECRET}`);


            const cloudinaryResponse = await clienteAxios.post(`https://api.cloudinary.com/v1_1/${process.env.CLOUDINARY_CLOUD_NAME}/image/destroy`, {
                public_id: `${process.env.CLOUDINARY_UPLOAD_PRESET}/${usuarioID}`,
                signature: signature,
                api_key: process.env.CLOUDINARY_API_KEY,
                timestamp: timestamp
            });

            if (imagen) {

            // Subir nuevo avatar a Cloudinary

            const image = new FormData()
            image.append('file', imagen)
            image.append('upload_preset', process.env.CLOUDINARY_UPLOAD_PRESET)
            image.append('public_id', usuarioID)


            const response = await clienteAxios.post(`https://api.cloudinary.com/v1_1/${process.env.CLOUDINARY_CLOUD_NAME}/image/upload`, image)

            setUrlImagen(response.data.secure_url)
             
            }


              const fechaNacimiento = `${dia.toString().padStart(2, '0')}-${mes.toString().padStart(2, '0')}-${year.toString()}`;
              

            const datosActualizados = {
                nombre,
                apellido,
                usuarioID,
                email,
                descripcion,
                fechaNacimiento,
                imagen: urlImagen
            };
                       

            const { data } = await clienteAxios.put(`/usuarios/perfil/${auth._id}`, datosActualizados)


            setAlertaVisible(true)
            setAlerta({
                msg: data.msg,
                error: false,
            })
            setTimeout(() => {
                setAlertaVisible(false)
            }, 3000);
        } catch (error) {
            console.log(error)
            setAlertaVisible(true)
            setAlerta({
                msg: error.response.data.msg,
                error: true,
            })

And here's the code of the usuarioController file in the backend:

const editarPerfil = async (req, res, next) => {


    const { nombre, apellido, email, descripcion, fechaNacimiento, usuarioID, imagen } = req.body;
  
    try {

        console.log(imagen)
        // Busca el usuario por su ID y actualiza los campos correspondientes

        let usuarioActualizado = await Usuario.findByIdAndUpdate(req.params.id,
            {
                nombre,
                apellido,
                email,
                descripcion,
                fechaNacimiento,
                usuarioID,
                imagen        
            },
            { new: true } // Devuelve el usuario actualizado
        );
        
  

        res.json({ msg: "Perfil actualizado correctamente", visible: true, usuarioActualizado });
    } catch (err) {
        console.log(err);
        const error = new Error("Error al editar el perfil del usuario")
        res.status(500).json({ msg: error.message, visible: true });
    }
};

The urlImagen state is not received by the controller the first time handleSubmit function is triggered, but works the second time. I need the state to be received by the controller so users can update profile data, including url of the cloudinary avatar image

Lucian
  • 21
  • 4
  • Does this answer your question? [The useState set method is not reflecting a change immediately](https://stackoverflow.com/questions/54069253/the-usestate-set-method-is-not-reflecting-a-change-immediately) – Konrad Jul 02 '23 at 21:29
  • Thanks Konrad. I actually checked that answer and also thought about using useEffect for the urlImagen state, but after doing so, it is returning an error. Do you happen to know where should I add it exactly? – Lucian Jul 02 '23 at 21:55

1 Answers1

-1

I really think that the issue here is related to the fact that you have called setUrlImagen in an async function. Because setUrlImagen updates asynchronously, the value may not be immediately updated and available when you access urlImagen in the datosActualizados object.

Now, if you want to make sure that you are using the updated urlImagen value when sending the PUT request, move the code that handles the request inside a useEffect hook with a dependency on urlImagen. So, the code will be triggered whenever urlImagen changes. Your modifed code:

...
// Define a useEffect hook
useEffect(() => {
  const handleSubmit = async () => {
    try {
     ...

      if (imagen) {
        // Subir nuevo avatar a Cloudinary
        const image = new FormData();
        image.append('file', imagen);
        image.append('upload_preset', process.env.CLOUDINARY_UPLOAD_PRESET);
        image.append('public_id', usuarioID);

        const response = await clienteAxios.post(
          `https://api.cloudinary.com/v1_1/${process.env.CLOUDINARY_CLOUD_NAME}/image/upload`,
          image
        );

        setUrlImagen(response.data.secure_url);
      }
      ...
  };

  handleSubmit(); 
}, [urlImagen]); // Add urlImagen as a dependency to the useEffect hook

This way, you'll have the updated urlImagen value in the datosActualizados object when making the request. Hopefully it'll work now without returning an error.