0

When I press "Submit" button the console shows "success" even though the input is empty, and when I press again it finally shows "error".

How do I change the code so that console will show "error" when I press the button for the first time?

import React, { useState } from "react";
import "./App.css";

function App() {
  const [input, setInput] = useState("");
  const [error, setError] = useState(false);
  const submitHandler = (e) => {
    e.preventDefault();
    if (input === "") {
      setError(true);
    }
    if (error) {
      console.log("error");
      return;
    }
    console.log("success");
  };
  return (
    <div className="App">
      <form onSubmit={submitHandler}>
        <input onChange={(e) => setInput(e.target.value)}></input>
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}

export default App;
tomleb
  • 2,409
  • 2
  • 9
  • 24

5 Answers5

0

Setting the state in React is an async function.
Meaning that the when you set the state and put a console.log right after it, like in your example, the console.log function runs before the state has actually finished updating.

Which is why we have useEffect, a built-in React hook that activates a callback when one of it's dependencies have changed.
Like so:

import { useState, useEffect } from 'react';

const [state, setState] = useState();

useEffect(() => {
   // Actions we want to happen when the state has been fully updated.
}, [state]);

For your example, it can look like this:

const submitHandler = (e) => {
   e.preventDefault();
   if (input === "") {
     setError(true);
   }
}

useEffect(() => {
   // No need for === true
   if (error) {
     console.log("error");
     return;
   }
   console.log("success");
}, [error])
tomleb
  • 2,409
  • 2
  • 9
  • 24
0

State change is async, you should check first submit against another state or a reference (useRef).

function App() {
  const [isFirstSubmit, setIsFirstSubmit] = useState(true);

  const submitHandler = (e) => {
    e.preventDefault();

    if (isFirstSubmit) {
      console.log("error");
      setIsFirstSubmit(false);
    }
  };
  return <form onSubmit={submitHandler}>...</form>;
}
Dennis Vash
  • 50,196
  • 9
  • 100
  • 118
0

Look again at the state declaration:

const [error, setError] = useState(false);

There is no way that calling setError is going to change error, because it's a const. The language doesn't allow it.

What happens is that the useState hook will return the value passed to setError the next time the component renders.

If you adjust your handleSubmit logic a bit, accounting for the fact that it knows when it calls setError(true), you can achieve the desired result:

  const submitHandler = (e) => {
    e.preventDefault();
    if (input === "") {
      setError(true);
      console.log("error");
      return;
    }
    console.log("success");
  };

All I did was remove two lines of code.

Steve
  • 8,066
  • 11
  • 70
  • 112
0

You can update your code to

if (input === "") {
  setError(true);
  console.log("error");
  return;
}

console.log("success");

So in first if you can handle error condition where after it will be success.

Ankit Patidar
  • 2,731
  • 1
  • 14
  • 22
0

It is not related to the asynchronous mechanism of the set state. It's all about closures. In the handler function, the value of error will equal its value during the last render (at the time when this function was actually created).

What setError(true) does is that it schedules a component re-render with the new value of error. The value of error will be true during the next render after setError was called.

Assuming that you just want to see a relevant message upon submit, you don't need the error state at all.

import * as React from "react";
import "./App.css";

function App() {
  const [input, setInput] = React.useState("");

  const submitHandler = (e) => {
    e.preventDefault();
    console.log(input === "" ? "error" : "success")
  };

  return (
    <div className="App">
      <form onSubmit={submitHandler}>
        <input onChange={(e) => setInput(e.target.value)}></input>
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}

export default App;
Georgy Nemtsov
  • 786
  • 1
  • 8
  • 19