【问题标题】:Wait for state to update when using hooks使用钩子时等待状态更新
【发布时间】:2021-07-28 07:34:42
【问题描述】:

如何使用 Hooks 等待状态更新。当我提交表单时,我需要在运行一些附加代码之前检查termsValidation 是否为假。如果状态刚刚改变,它就不会注意到这一点。

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>
  );
}

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    useState 钩子是异步的,但它没有像 setState 那样的回调 API。如果你想等待状态更新,你需要一个 useEffect 钩子:

    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>
      );
    }
    

    【讨论】:

    • 小心一个循环。可以超过更新深度,无限渲染
    【解决方案2】:

    setTermsValidation 这样的改变状态是异步操作,这意味着它不是立即的,程序也不会等待它。它开火然后忘记。因此,当您调用 setTermsValidation(true) 时,程序将继续运行下一个块,而不是等待 termValidation 变为 true。这就是为什么 termsValidation 仍将具有旧值的原因。

    你可以这样做

    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');
        }
    }
    

    或者最好使用钩子useEffect()

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

    但是,要小心,因为 useEffect 也会在初始渲染时运行。

    【讨论】:

      【解决方案3】:

      不要忘记在这种情况下可能会使用 useRef - useState 和 useEffect 当然有它们的位置,但是您跟踪和管理状态的逻辑可能会有点麻烦,并且可能会导致不必要的重新渲染您的组件(当该状态不构成渲染输出的一部分时)。作为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
        );
      }
      

      【讨论】: