Link
Link

Reputation: 371

useState doesn't update its value

I'm creating a input form for an e-mail and i have a delayed onChange on it to not call the api too many times.

Here's my code:

const InformationCollection = (props) => {
  const [email, setEmail] = useState()
  const [collectedEmail, setCollectedEmail] = useState(1)

  useEffect(() => {
    let timeout = setTimeout(() => {
      setCollectedEmail(email)
      console.log(collectedEmail)
    }, 500)
    return () => {
      clearTimeout(timeout)
    }
  }, [email])

    return (
        <div className="form-group">
          <label htmlFor="inputmail">Email address</label>
          <input
            type="email"
            className="form-control"
            onChange={(e) => {
              setEmail(e.target.value)
              console.log(e.target.value + "this is what is set" + email)
            }}
            aria-label="Enter e-mail address"
          />
        </div>
    )
}

export default InformationCollection

On this line if i type "1" console.log(e.target.value + "this is what is set" + email), e.target.value is 1, but email is undefined.

On the next character "12", e.target.value is 12 but email is 1

Can anyone help with this?

UPDATE:

The solution is to have 2 useEffectHooks. One for the value in the form email and one for the delayed value collectedEmail

Second solution is to do fetch inside the first useEffect hook

const InformationCollection = (props) => {
  const [email, setEmail] = useState()
  const [collectedEmail, setCollectedEmail] = useState()

  useEffect(() => {
    let timeout = setTimeout(() => {
      //fetch directly here
      setCollectedEmail(email)
      console.log(collectedEmail)
    }, 500)
    return () => {
      clearTimeout(timeout)
    }
  }, [email])

  useEffect(() => {
    //fetch() here
    console.log(collectedEmail) //right value
  }, [collectedEmail])

    return (
        <div className="form-group">
          <label htmlFor="inputmail">Email address</label>
          <input
            type="email"
            className="form-control"
            onChange={(e) => {
              setEmail(e.target.value)
              console.log(e.target.value + "this is what is set" + email)
            }}
            aria-label="Enter e-mail address"
          />
        </div>
    )
}

export default InformationCollection

Upvotes: 2

Views: 86

Answers (2)

Yousaf
Yousaf

Reputation: 29282

state is updated asynchronously, that's why email is undefined for the first time when you try to log it after updating the state.

You can log the email inside useEffect hook which will be called after email has changed.

On the next character "12", e.target.value is 12 but email is 1

email is 1 because when onChange event fired for the first time, email was undefined but when onChange event fires for the second time, email had already been updated asynchronously to 1

Upvotes: 2

Davin Tryon
Davin Tryon

Reputation: 67296

Isn't this expected behaviour? email is always the value before the change inside the onChange handler. Because the re-render hasn't happened yet.

To see the value rendered do this:

    return (
        <div className="form-group">
          <label htmlFor="inputmail">Email address: { email }</label>
          <input
            type="email"
            className="form-control"
            onChange={(e) => {
              setEmail(e.target.value)
              console.log(e.target.value + "this is what is set" + email)
            }}
            aria-label="Enter e-mail address"
          />
        </div>
    )

Upvotes: 2

Related Questions