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

如何在表单提交函数中使用useAsync hook解决Hook规则调用报错

问题原因
  • 所有React Hook只能在组件顶层作用域或者自定义Hook内部调用,你在事件处理函数handleSubmit里调用useAsync、useFetch两个Hook,直接违反Hook规则,所以触发报错。
  • 尝试把onSubmit改为首字母大写的做法完全不对:首字母大写的函数在React中会被识别为组件,事件处理函数是普通回调,不需要也不能改成大写命名,所以会触发运行时错误。
  • react-async提供的useAsync默认是组件渲染时自动执行异步逻辑,不适合手动触发的表单提交场景,你需要用同库提供的useAsyncFn,它会返回手动触发执行的run方法,符合你的需求。
解决步骤
  1. 先把原本封装在useFetch Hook里的请求逻辑抽成普通的返回Promise的工具函数,不要用Hook封装
  2. 在组件顶层调用useAsyncFn,传入promiseFn定义请求逻辑,接收返回的执行函数和状态
  3. 在handleSubmit中调用返回的执行函数,传入表单参数触发请求
修改后代码
import { Modal } from 'react-bootstrap';
import "react-datepicker/dist/react-datepicker.css";
import DatePicker from 'react-datepicker'
import { useState } from 'react';
// 导入useAsyncFn替代useAsync
import { useAsyncFn } from 'react-async';
import { useLocation } from 'react-router-dom';

// 抽离为普通请求工具函数,替换原来的useFetch自定义Hook
const request = async (url: string, method: string, data: any) => {
  const res = await fetch(url, {
    method,
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
  })
  if (!res.ok) throw new Error('请求失败')
  return res.json()
}

const TodoPopup = (props : {show : boolean, onHide : () => void, title : string, values? 
: {title : string, expirationDate : any, description : string}}) => {

  const [name, setName] = useState(props.values?.title || '');
  const [date, setDate] = useState(props.values?.expirationDate || '');
  const [description, setDescription] = useState(props.values?.description || '');

  const url = useLocation().pathname;

  // 组件顶层调用useAsyncFn,定义请求逻辑
  const [{ data, error, isPending }, runRequest] = useAsyncFn(async (name: string, date: Date, description: string) => {
    const method = props.values ? 'PUT' : 'POST'
    return request(url, method, {name, date, description})
  }, [url, props.values]) // 声明依赖项

  const handleSubmit = (e : React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    // 手动触发提交请求
    runRequest(name, date, description)
  }

  // 处理请求状态
  if(isPending) console.log('loading...');
  if(error) console.log('error');
  if(data)  props.onHide();

  return (
    <Modal {...props} centered>
      <Modal.Header closeButton>
        <Modal.Title>{props.title}</Modal.Title>
      </Modal.Header>
      <form onSubmit={handleSubmit}>
        <Modal.Body>
          <div className = "form-group">
              <label>Task Name</label>
              <input type="text" className = "form-control" value = {name} onChange = {(e) => setName(e.target.value)} required />
          </div>
          <div className = "form-group">
              <label>Expired</label>
              <DatePicker selected={date} onChange={e => setDate(e)} className="form-control" minDate={new Date()} required />
          </div>
          <div className = "form-group">
              <label>Description</label>
              <textarea rows = {5} className = "form-control" value = {description} onChange = {(e) => setDescription(e.target.value)}></textarea>
          </div>
        </Modal.Body>
        <Modal.Footer>
          <button className="btn btn-primary" type="submit">Save changes</button>
        </Modal.Footer>
      </form>
    </Modal>
  );
};

export default TodoPopup;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 23:45:03