Search code examples
javascriptreactjsreact-hookssettimeoutgatsby

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

Solution

  • 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