'useState is not updating the state in React

ChangeHandler updates the state fine.

However, updating the state of id on onSubmit does not seem to update the id.

Code is:

import { useState } from "react"

export default function Form(props) {
    const [userInput, setUserInput] = useState({
        name: "",
        url: "",
        id: ""
    })

    const ChangeHandler = (e) => {
        const { name, value } = e.target

        setUserInput((prevState) => {
            return {
                ...prevState,
                [name]: value
            }
        })
    }

    const clearForm = () => {
        setUserInput({
            name: "",
            url: "",
            id: ""
        })
    }

    const onSubmit = (e) => {
        e.preventDefault()

        setUserInput(prevState => {
            return {
                ...prevState,
                id: Math.floor(Math.random() * 100) 
            } //does not work?
        });

        props.onFormData(userInput)
        clearForm()
    }

    return (
        <div>
            <form onSubmit={onSubmit}>
                <input name="name" className="input" type="text" value={userInput.name} placeholder="Name" onChange={ChangeHandler}></input>
                <input name="url" className="input" type="text" value={userInput.url} placeholder="url" onChange={ChangeHandler}></input>
                <button type="submit" className="button is-primary" >Send</button>
            </form>
        </div>
    )
}


Solution 1:[1]

You don't need to use setUserInput. Just sand parms like this:

props.onFormData({
  ...userInput,
  id: Math.floor(Math.random() * 100)
})

When use change state, the state will applies on next render. That's why you can't use updated state.

Solution 2:[2]

When you do

setUserInput((prevState) => {
  return {
    ...prevState,
    id: Math.floor(Math.random() * 100),
  };
});
props.onFormData(userInput);

userInput will not have updated to the new value you set yet. The new value will be available on the next render, until then userInput will still give you the old value. If it helps, you can think of setUserInput() as a deferred function that will resolve the update later.

You can sidestep the problem by putting your new state in a temporary variable which you can use for both purposes.

const newUserInput = {
  ...userInput,
  id: Math.floor(Math.random() * 100),
};
setUserInput(newUserInput);
props.onFormData(newUserInput);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 leafy
Solution 2 Etheryte