Search code examples
reactjsfetchuse-state

Trying to set & send a state variable as an URL string in a POST request with a single button click


Two buttons with its own function excuected when clicked, one is savedays and other is submitdays. I want to combine the functions so its only one button.

  const [days, setDays] = useState({
    Monday: false,
    Tuesday: false,
    Wednesday: false,
    Thursday: false,
    Friday: false,
    Saturday: false,
    Sunday: false,
  });
  const [selectedDays, setSelectedDays] = useState("");

  const saveDays = async () => {
    await setSelectedDays((selectedDays) => "");

    if (days.Monday == true) {
      await setSelectedDays((selectedDays) => selectedDays.concat("MON,"));
    }
    if (days.Tuesday == true) {
      await setSelectedDays((selectedDays) => selectedDays.concat("TUE,"));
    }
    if (days.Wednesday == true) {
      await setSelectedDays((selectedDays) => selectedDays.concat("WED,"));
    }
    if (days.Thursday == true) {
      await setSelectedDays((selectedDays) => selectedDays.concat("THUR,"));
    }
    if (days.Friday == true) {
      await setSelectedDays((selectedDays) => selectedDays.concat("FRI,"));
    }
    if (days.Saturday == true) {
      await setSelectedDays((selectedDays) => selectedDays.concat("SAT,"));
    }
    if (days.Sunday == true) {
      await setSelectedDays((selectedDays) => selectedDays.concat("SUN,"));
    }
    await setSelectedDays((selectedDays) => selectedDays.slice(0, -1));
    console.log(selectedDays, "updated");
  };

  const submitDays = async () => {
    let postRes = await fetch(`/addSurveyDays/${selectedDays}`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
    });
    let postResult = await postRes.json();
    if (postResult.success) {
      console.log("succes posted");
    } else console.log("Something went wrong, please try again");
  };

      <button
        type="button"
        className="greyBtn"
        id="saveBtn"
        onClick={async () => {
          await saveDays();
        }}
      >
        Save
      </button>
      <button
        type="button"
        className="greyBtn"
        id="saveSubmitBtn"
        onClick={async () => {
          await submitDays();
        }}
      >
        Submit
      </button>

But when i combine both this functions to be executed on click with a single button. I get this error the first time i run it.

Chrome console log, "POST HTTP://localhost:3000/addSurveyDays/ 404 (Not Found)

The error is only there the first time i run it, then its fine. can someone help explain why that is?

There are 7 other buttons, each one for the day of the week and when clicked turn their respective day on. Example: days.Monday would turn true when its button is clicked


Solution

  • Simply (1) add your logic for building the selected days string into a click handler and then (2) trigger an effect which will send a POST request after each change of the selectedDays state variable.

    const App = () => {
      const [days, setDays] = useState({
        Monday: false,
        Tuesday: false,
        Wednesday: false,
        Thursday: false,
        Friday: false,
        Saturday: false,
        Sunday: false,
      });
      const [selectedDays, setSelectedDays] = useState("");
    
      useEffect(async () => {
        let postRes = await fetch(`/addSurveyDays/${selectedDays}`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "Accept": "application/json",
          },
        });
        let postResult = postRes.json();
        if (postResult.success) {
          console.log("succes posted");
        } else {
          console.log("Something went wrong, please try again");
        }
      }, [selectedDays]);
    
      const handleSaveAndSubmit = () => {
        let newSelectedDays = "";
    
        Object.keys(days).forEach((day) => {
          if (days[day]) {
            newSelectedDays = newSelectedDays.concat(
              day.substring(0, 3).toUpperCase() + ","
            );
          }
        });
        console.log(newSelectedDays, "updated");
        setSelectedDays(newSelectedDays.slice(0, -1));
      };
    
      return (
        <button
          type="button"
          className="greyBtn"
          id="saveSubmitBtn"
          onClick={handleSaveAndSubmit}
        >
          Save and Submit
        </button>
      );
    };
    

    The error is only there the first time i run it, then its fine. can someone help explain why that is?

    That's simply due to the fact that you can't access the updated state variable in a React functional component from your current scope.

    In your case, you're assuming that selectedDays gets updated when you call setSelectedDays() and that you can access the updated value from your current state. You can't do that. It doesn't matter how long you wait (note that putting await in front of the state update functions has no effect, since they're not Promisees).

    See: useState set method not reflecting change immediately for more info.