bp123
bp123

Reputation: 3417

Wait for state to update when using hooks

How do I wait for state to update using Hooks. When I submit my form I need to check if termsValidation is false before running some additional code. If the state has just changed it doesn't pick up on this.

import React, { useState } from 'react';

export default function Signup() {
  const [terms, setTerms] = useState('');
  const [termsValidation, setTermsValidation] = useState(false);

  function handleSubmit(e) {
    e.preventDefault();

    if (!terms) {
      setTermsValidation(true);
    } else {
      setTermsValidation(false);
    }

    if (!termsValidation) {
      console.log('run something here');
    }
  }

  return (
    <div>
      <form>
        <input type="checkbox" id="terms" name="terms" checked={terms} />

        <button type="submit" onClick={handleSubmit}>
          Sign up
        </button>
      </form>
    </div>
  );
}

Upvotes: 42

Views: 95431

Answers (3)

Andy Lorenz
Andy Lorenz

Reputation: 3084

Don't forget useRef as a possibility in situations like this - useState and useEffect have their place of course, but your logic to track and manage the state can be a bit of a pain, as well as causing probable unnecessary re-renders of your component (when that state doesn't form part of the render output). As an example from the OP:

import React, { useState, useRef } from 'react';

export default function Signup() {
  const [terms, setTerms] = useState('');
  const termsValidation = useRef(false);

  function handleSubmit(e) {
    e.preventDefault();

    if (!terms && !termsValidation.current) {
      termsValidation.current = true;
      console.log('run something here');
      termsValidation.current = false; // when its finished running
    }
 }

  return (
    <div> etc
  );
}

Upvotes: 6

Gianfranco Fertino
Gianfranco Fertino

Reputation: 632

Changing state like setTermsValidation is asynchronous action which means it's not immediate and the program does not wait for it. It fires and forgets. Hence, when you call setTermsValidation(true) the program will continue run the next block instead of waiting termValidation to change to be true. That's why termsValidation will still have the old value.

You can just do this

function handleSubmit(e) {
    e.preventDefault();

    if (!terms) {
      setTermsValidation(true);
    } else {
      setTermsValidation(false);
      // assuming you want to run something when termsvalidation turn to false
      console.log('run something here');
    }
}

Or ideally use hooks useEffect()

useEffect(() => {
    if (!termsValidation) {
      console.log('run something here');
    }
}, [termsValidation]);

However, be careful because useEffect also runs on initial render.

Upvotes: 22

DoHn
DoHn

Reputation: 673

The useState hook is asynchronous but it doesn't have a callback api like setState does. If you want to wait for a state update you need a useEffect hook:

import React, { useState, useEffect } from 'react';

export default function Signup() {
  const [terms, setTerms] = useState('');
  const [termsValidation, setTermsValidation] = useState(false);

  useEffect(() => {
    if (!termsValidation) {
      console.log('run something here');
    }
  }, [termsValidation]);

  function handleSubmit(e) {
    e.preventDefault();

    if (!terms) {
      setTermsValidation(true);
    } else {
      setTermsValidation(false);
    }
  }

  return (
    <div>
      <form>
        <input type="checkbox" id="terms" name="terms" checked={terms} />

        <button type="submit" onClick={handleSubmit}>
          Sign up
        </button>
      </form>
    </div>
  );
}

Upvotes: 40

Related Questions