0

I was trying to send data from frontend(React) to backend(express) from a HTML form, and clear the fields after submit. This is what it looks like. Here item and amount are state of controlled components. So my question is as I'm updating state before fetch its not updating it. As fetch and setState are async so which one will get executed first. Even if I write state updates after fetch then also it seems like fetch is executed first. But I'm not getting how ?

function onSub(e, setList, list) {
e.preventDefault();


setList([...list, { item, amount }]);
setItem("");
setAmt("");


  fetch("http://localhost:3001/addData", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ title: item, amt: amount }),
  })
    .then((res) => res.text())
    .then((data) => {
      console.log(data);
    });

}

Nakul Gopal
  • 60
  • 1
  • 4

1 Answers1

1

They're in a race. Normally you'd expect the state to get set first, since the state setters are operating just locally within the DOM and the fetch has to go talk to a server, but they're in a race and you can't count on which one is going to win the race.

If you want to control which happens first, you need to do that explicitly. It's easier to make the fetch finish first since you can just wait to call the state setters until the end of the fetch process. That also gives you the opportunity to handle a fetch failure (transient network error, for instance) without losing the information the user provided because you've already cleared it from state:

function onSub(e, setList, list) {
    e.preventDefault();

    fetch("http://localhost:3001/addData", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({ title: item, amt: amount }),
    })
    .then((res) => {
        if (!res.ok) { // This check was missing
            throw new Error(`HTTP error ${res.status}`);
        }
        return res.text();
    })
    .then((data) => {
        console.log(data);
        setList([...list, { item, amount }]);
        setItem("");
        setAmt("");
    })
    .catch(error => {
        setError(/*...some error message saying something went wrong...*/);
    });
}

Side note: I explain the comment about the check that was missing here. To avoid having to write all that boilerplate, I suggest having a few handy utility functions lying around, for instance:

export async function postJSON(url, data) {
    const res = await fetch(url, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify(data),
    });
    if (!res.ok) { // This check was missing
        throw new Error(`HTTP error ${res.status}`);
    }
    return res;
}

Then the code would look like this:

function onSub(e, setList, list) {
    e.preventDefault();

    postJSON("http://localhost:3001/addData", { title: item, amt: amount })
    .then((data) => {
        console.log(data);
        setList([...list, { item, amount }]);
        setItem("");
        setAmt("");
    })
    .catch(error => {
        setError(/*...some error message saying something went wrong...*/);
    });
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875