As I write here, I will show my errors using React with JavaScript. And I will put also my answers or the solution to that error, but doesnt mean is actually correct, because I am learning, but for me made sense, and also I look for youtube videos with seniors engineers using React, how they approach this error, and I think follows the principles that taught me in their videos. And thanks to @midudev, and great teacher.

Fetch errors so simple

The following code works, but doesnt catch the errors, and can break the system if it continues like this:

export const createUser = async (user_data) => {
    const response = await fetch(CREATE_USER_EP,{
        method: "POST",
        body: JSON.stringify(user_data),
        headers:{
            "Content-Type": "application/json",
        }
    });
    const data = await response.json();
    let status = response.status;
    return { status, data}
}

And the solution is control the errors I know can break the system:

export const createUser = async (user_data) => {
    let result = {created:false, error: null, message: null, code: null};
    try{
        const response = await fetch(CREATE_USER_EP,{
            method: "POST",
            body: JSON.stringify(user_data),
            headers:{
                "Content-Type": "application/json",
            }
        });
        if(!response.ok){
            result = {created:false, error: `Ocurrio un error del sistema, codigo: 
            ${response.status}, lo sentimos`, code: response.status}
            return result;
        }
        const data = await response.json();
        console.log("data: ", data);
        return {created: true, error: null, message: data, status: response.status}
    }catch(error){
        throw new Error("Error de comunicacion con nuestros servicios, lo 
        sentimos.")
    }
}

The same error but different fetch to the API:

export const getShops = async () => {
    const response = await fetch(LIST_SHOPS);
    const data = await response.json();
    let ok = response.ok;
    console.log(data);
    return {ok, data};
}

And the solution: control the errors, I also have added console.logs to see if it's working, to test.

export const getShops = async () => {
    let result = {success: false, error: '', data: {}}
    try{
        const response = await fetch(LIST_SHOPS);
        if(!response.ok){
	        return {...result, 
	        error: `codigo: ${response.status}, ocurrio un error en los servicios, lo sentimos`}
        }
        const data = await response.json();
        console.log("getShops function, data: ", data);
        return {...result, success: true, data: data}
    }catch(error){
        throw new Error("Error conectandose a nuestro servicios");
    }
}

Redundant code that doesnt make sense

import { useState, useEffect } from 'react';
import { useMultiStepForm } from '../../context/FormProvider';
import InfoButton from '../../components/GetFormData';
import  '../../static/css/house/upload_add.css';
const MAX_FILE_MB = 6;
const ONE_MB = 1000 * 1000;
const MAX_FILE_SIZE = MAX_FILE_MB * ONE_MB;

export default function UploadFile() {
    const { formData, updateFormData, nextStage } = useMultiStepForm();
    const [message, setMessage] = useState(["", ""]);
    const [fileName, setFileName] = useState("");
    const [selectedFile, setSelectedFile] = useState(null);

    useEffect(() => {
        if (formData?.post && formData.post?.file) {
            setSelectedFile(formData.post.file);
        }
    }, [formData]);

    function handleChange(e){
        const file = e.target.files[0];
        if(!file) return;
        setMessage(['','']);
        if(file.size > MAX_FILE_SIZE){
            setMessage(['',`El tamaño del archivo supera los ${MAX_FILE_MB} MB`]);
            e.target.value = '';
            setSelectedFile(null);
            updateFormData({post: {file: null}});
            return;
        }
        setSelectedFile(file);
        updateFormData({post: {file:file}})
        const sizeMB = (file.size / ONE_MB).toFixed(2);
        setMessage([`Archivo valido: ${sizeMB} MB`,'']);
    }

    function save_file(e) {
        e.preventDefault();
        if(!selectedFile){
            setMessage(["No se ha seleccionado ningun archivo.", ""]);
        }else{
            nextStage();
        }
    }

    return (
    <div>
        <form className='upload-add-form' onSubmit={save_file}>
            <label  htmlFor='file'>Elija un archivo pdf de su anuncio</label>
            <input 
                type='file' 
                name='file' 
                accept='.pdf' 
                onChange={handleChange}
            />
            {selectedFile && (
                <div className='file-info'>
                    <p>Archivo seleccionado: {selectedFile.name}</p>
                </div>
            )}

            {message[0] && <p className="message-error">{message[0]}</p>}
            {message[1] && <p className="message-info" style={{ color: "red" }}>
            {message[1]} MB</p>}

            <button type="submit">Siguiente</button>
        </form>
        {/* <InfoButton /> */}

    </div>
    );
}

And the errors are:

  1. Doesn't have meaningful names in the error messages: ["error1","error2"].
  2. And I find that is some kind of unnecesary complexity using selectedFile and formData.

The first error, is that to upload the file, in the handleChange() method, I update to states:

setSelectedFile(null);
updateFormData({post: {file: null}});
-----
setSelectedFile(file);
updateFormData({post: {file:file}})

But if I think clearly, we just have one goal here: Users need to upload a file, and happens when the "Siguiente" button is clicked.

And to keep track those changes, I dont need to update the states, just one, and that is `selectedFile` and not the `updateFormData`.

One more thing, I have a `useEffect`:

useEffect(() => {
        if (formData?.post && formData.post?.file) {
            setSelectedFile(formData.post.file);
        }
    }, [formData]);

So formData relies on selectedFile to give the current file. And doesn't makes sense to be on the contrary.I just update formData when "Siguiente" button is clicked. And I update selectedFile only on the onChange event is triggered.

Another error is in an array, the first is for info message and other for errors message.

const [message, setMessage] = useState(["", ""]);

But it's implicit meaning, so I need to put explicit meaning:

const [message, setMessage] = useState({
        info: '',
        error_message: ''
    });

And if the users decides to go back to this form, when it's in the next form, I will put by default the file previously stored.

const [selectedFile, setSelectedFile] = useState(formData?.post?.file);

And the final version with all these changes is:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import { useState, useEffect } from 'react';
import { useMultiStepForm } from '../../context/FormProvider';
import InfoButton from '../../components/GetFormData';
import  '../../static/css/house/upload_add.css';
const MAX_FILE_MB = 6;
const ONE_MB = 1000 * 1000;
const MAX_FILE_SIZE = MAX_FILE_MB * ONE_MB;

export default function UploadFile() {
    const { formData, updateFormData, nextStage } = useMultiStepForm();
    const [message, setMessage] = useState({
        info: '',
        error_message: ''
    });
    const [selectedFile, setSelectedFile] = useState(formData?.post?.file);

    function handleChange(e){
        const toMB = (file) => { return (file.size / ONE_MB).toFixed(2)}
        const file = e.target.files[0];
        if(!file) return;
        setMessage({info: '', error_message: ''});
        if(file.size > MAX_FILE_SIZE){
            setMessage({
                info: '', error_message: `El tamaño del archivo supera los 
                ${MAX_FILE_MB} MB. Tiene ${toMB(file)} MB`
            });
            e.target.value = '';
            setSelectedFile(null);
            return;
        }
        setSelectedFile(file);
        setMessage({info: `Archivo valido: ${toMB(file)}`, error_message: ''});
    }

    function save_file(e) {
        e.preventDefault();
        if(message.error_message){
            alert("No puede seguir, si hay un error");
            return;
        }
        if(!selectedFile){
            setMessage({info: "No se ha seleccionado ningun archivo.", 
            error_message: ''});
        }else{
            updateFormData({post: {file: selectedFile}});
            nextStage();
        }
    }

    return (
    <div>
        <form className='upload-add-form' onSubmit={save_file}>
            <label  htmlFor='file'>Elija un archivo pdf de su anuncio</label>
            <input 
                type='file'
                name='file'
                accept='.pdf'
                onChange={handleChange}
            />
            {selectedFile && (
                <div className='file-info'>
                    <p>Archivo seleccionado: {selectedFile.name}</p>
                </div>
            )}

            {message.error_message && <p className="message-error">
            {message.error_message}</p>}
            {message.info && <p className="message-info" style={{ color: "red" }}>
            {message.info} MB</p>}

            <button type="submit">Siguiente</button>
        </form>
    </div>
    );
}

I will edit this, maybe doesnt understand well, and if you have any doubt please contact me. I hope this is helpful to understand what I did to improve, and it's just tiny things that together can be more reliable your code also. I dont touch yet the tests(an error), but I will continue to improve this. 😁👍👍