Reputation: 3417
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
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
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
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