3

I am updating state using React useState hook on click of a form submit button, the state is not updating until second click. Believe I need to use a useEffect but not sure how to implement with an onClick.

const files = [
  {
    id: 'foo'
  }
]

const [fieldName, setName] = useState({});


const onSubmit = () => {
    files.map((file) => {
      if (!fieldName[file.id]?.value) {
        setName(() => ({
          ...fieldName,
          [file.id]: {
            value: test[file.id]?.value,
            error: true
          },
        }));
      }
    });

    console.log(fieldName);
    // Output = {}
    // Expected Output = { foo: { error: true } }
    
    if (fieldName) // simply to show i need access to updated fieldName at this point

  };
James
  • 281
  • 3
  • 9

2 Answers2

4

As mentioned in the comments; useState is an async function and thus does not garantuee you'll get the new value upon retrieving its value again. This is for performance reasons allowing batching of multiple state changes.

The easiest way to keep working with the new value within the same method is simply saving it into a variable as follows:

const [clicked, setClicked] = useState(false);

function onclick(e) {
  setClicked(true);
  if (clicked) { //this goes wrong as clicked likely isn't updated yet
    //do something
  }
}
const [clicked, setClicked] = useState(false);

function onclick(e) {
  const newClicked = true;
  setClicked(newClicked);
  if (newClicked) { //using the variable garantuees you'll get the value you want
    //do something
  }
}
NotFound
  • 5,005
  • 2
  • 13
  • 33
0

I implemented a useEffect to set a hasError state

useEffect(() => {
    const hasErrors = Object.keys(fieldName).filter(
      (key) => fieldName[key].error
    );
    if (!hasErrors.length) {
      setFormHasError(() => true);
    }
  }, [fieldName]);

I could then use the following in code

if (formHasError) {
      return;
    }
James
  • 281
  • 3
  • 9