You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React表单提交需点击两次,如何让函数读取更新后的state?

问题根源

  • 你调用的yup的validate、isValid都是异步Promise方法,forEach不会等待这些异步操作执行完成,你加的setTimeout(handleInfoCheck)无法保证执行时机是在所有校验完成、state更新之后
  • React的状态更新是批量异步的,在同一个事件循环周期内调用的setIsFormsValid不会立即更新当前作用域下的isFormsValid变量,所以handleInfoCheck读取的永远是本次点击前的旧状态

解决方法

方案1:收集所有校验Promise,等全部完成后直接判断校验结果,不依赖异步更新的state

修改后的代码如下:

const handleValid = async () => {
  const filteredMembers = members
    .filter((member: Tourist) => {
      return member.createdIn === touristCreatedIn && !member.isEmployee;
    });
  
  // 先处理错误提示的校验
  filteredMembers.forEach((member: any, index: any) => {
    personSchema
      .validate(member, { abortEarly: false })
      .then(() => {
        setFieldError({
          [index]: {}
        })
      })
      .catch((errs: any) => {
        setFieldError({})
        errs?.inner?.forEach((err: any) => {
          setFieldError((prev)=> ({
            ...prev,
            [index]: {
              ...prev[index],
              [err.path]: err.message,
            },
          }))
        });
      });
  });

  // 收集所有校验合法性的Promise
  const validPromises = filteredMembers.map((member: any) => {
    return personSchema.isValid(member)
      .catch((err: any) => {
        console.error('TouristData YUP isValid Err', err);
        return false;
      });
  });

  // 等待所有校验完成
  const validResults = await Promise.all(validPromises);
  // 直接判断所有结果是否合法,不用读state
  const allValid = validResults.every(v => v === true);

  // 先更新状态,再执行跳转逻辑
  filteredMembers.forEach((member, index) => {
    handleSelfValidation(validResults[index], index);
  });

  setFirstVisit();
  if (allValid) {
    switch (permissionType) {
      case 'tour':
        history.push(`${addTourUrl}/tour-data`);
        break;
      case PERMISSION_TYPE_TRANZIT:
        history.push(`${addTourUrl}/tranzit-data`);
        break;
      default:
        history.push(`${addTourUrl}/tour-data`);
        break;
    }
  }
};

const handleSelfValidation = (isFormValid: boolean, formIndex: number) => {
  console.log(isFormValid, formIndex, 'test')
  setIsFormsValid((prev) => ({
    ...prev,
    [formIndex]: isFormValid,
  }))
};

// 不需要在handleValid里调用handleInfoCheck了,逻辑已经整合进去
const handleInfoCheck = () => {
  setFirstVisit();
  if (Object.values(isFormsValid).every((item: any) => item === true)) {
    switch (permissionType) {
      case 'tour':
        history.push(`${addTourUrl}/tour-data`);
        break;
      case PERMISSION_TYPE_TRANZIT:
        history.push(`${addTourUrl}/tranzit-data`);
        break;
      default:
        history.push(`${addTourUrl}/tour-data`);
        break;
    }
  }
};

方案2:通过useEffect监听状态变化触发跳转

如果业务需要必须依赖isFormsValid的状态执行后续逻辑,可以用useEffect监听状态更新后再执行跳转:

// 新增useEffect监听
useEffect(() => {
  if (!firstVisit) return;
  if (Object.values(isFormsValid).every((item: any) => item === true)) {
    switch (permissionType) {
      case 'tour':
        history.push(`${addTourUrl}/tour-data`);
        break;
      case PERMISSION_TYPE_TRANZIT:
        history.push(`${addTourUrl}/tranzit-data`);
        break;
      default:
        history.push(`${addTourUrl}/tour-data`);
        break;
    }
  }
}, [isFormsValid, firstVisit, permissionType, history, addTourUrl])

// handleValid只需要做校验更新状态即可
const handleValid = () => {
  const filteredMembers = members
    .filter((member: Tourist) => member.createdIn === touristCreatedIn && !member.isEmployee);
  
  filteredMembers.forEach((member: any, index: any) => {
    personSchema
      .validate(member, { abortEarly: false })
      .then(() => setFieldError({ [index]: {} }))
      .catch((errs: any) => {
        setFieldError({})
        errs?.inner?.forEach((err: any) => {
          setFieldError(prev => ({
            ...prev,
            [index]: { ...prev[index], [err.path]: err.message }
          }))
        })
      })

    personSchema.isValid(member)
      .then(v => handleSelfValidation(v, index))
      .catch(err => console.error('TouristData YUP isValid Err', err))
  });
  setFirstVisit();
};

内容的提问来源于stack exchange,提问作者bazmexes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 17:45:01