React中onClick事件触发自定义Hook方法及无效调用报错解决
问题根因
你对自定义Hook的使用规则存在认知偏差,同时代码有多处不符合规范的问题:
- React 明确要求自定义Hook只能在函数组件的顶层作用域调用,不能直接在事件回调、循环、条件判断中执行
- 你当前写的
sendEmail本质是普通异步工具函数,不是符合规范的自定义Hook(React 要求自定义Hook命名必须以use开头,比如useSendEmail) - 报错的核心原因是要么你没贴全代码、
sendEmail内部实际调用了React内置Hook,要么项目的Hook检测规则把该函数误判为自定义Hook,检测到你在回调里调用就抛出了错误
修复方案
方案1:直接作为普通工具函数使用(最适配你的场景)
你的sendEmail没有用到任何React内置Hook,完全不需要封装成自定义Hook,直接调整代码即可解决报错:
- 修正
customHook.ts的未定义变量问题,保持普通函数导出:
// 你之前的mail变量未定义,此处补充默认配置,可根据业务调整 interface MailConfig { subject: string; body: string; } const defaultMail: MailConfig = { subject: "咨询反馈", body: "感谢您的联系,我们会尽快回复" } async function sendEmail(userName: string, userEmail: string, userPhone: string, mail: MailConfig = defaultMail) { const mailToUser = { to: userEmail, subject: mail.subject, body: mail.body, }; await fetch(`/api/sendEmail`, { method: `POST`, headers: { 'Content-Type': `application/json` }, body: JSON.stringify(mailToUser), }); console.log(mailToUser); } export default sendEmail;
- 修正
contact.tsx的语法错误、导入路径,补充异常捕获:
// 调整为你实际的customHook.ts存放路径,比如src/utils/customHook import sendEmail from 'src/utils/customHook' // 之前的组件定义少了箭头函数的=>,此处修正 export const Contact = (props:any) => { const userName = `Name`; const userEmail = `email`; const userPhone = `333333333`; const handleClick = async () => { try { await sendEmail(userName, userEmail, userPhone) alert("邮件发送成功") } catch (err) { console.error("邮件发送失败", err) alert("邮件发送失败,请稍后重试") } } return ( <button onClick={handleClick}>Contact</button> ) }
方案2:封装为规范自定义Hook(需要关联组件状态时使用)
如果后续需要在发送逻辑中关联加载状态、错误提示等组件状态,可以封装为标准自定义Hook使用:
- 调整
customHook.ts为符合规范的自定义Hook:
import { useState } from "react" interface MailConfig { subject: string; body: string; } const defaultMail: MailConfig = { subject: "咨询反馈", body: "感谢您的联系,我们会尽快回复" } // 自定义Hook必须以use开头命名 const useSendEmail = () => { // 仅在Hook顶层调用React内置Hook const [loading, setLoading] = useState(false) const [error, setError] = useState<Error | null>(null) // 将触发发送的逻辑作为函数返回,供组件在回调中调用 const send = async (userName: string, userEmail: string, userPhone: string, mail: MailConfig = defaultMail) => { setLoading(true) setError(null) try { const mailToUser = { to: userEmail, subject: mail.subject, body: mail.body, }; await fetch(`/api/sendEmail`, { method: `POST`, headers: { 'Content-Type': `application/json` }, body: JSON.stringify(mailToUser), }); console.log(mailToUser); } catch (err) { setError(err as Error) throw err } finally { setLoading(false) } } return { send, loading, error } } export default useSendEmail;
- 组件中按规则使用自定义Hook:
import useSendEmail from 'src/utils/customHook' export const Contact = (props:any) => { const userName = `Name`; const userEmail = `email`; const userPhone = `333333333`; // 在组件顶层调用自定义Hook,拿到发送方法和状态 const { send: sendEmail, loading, error } = useSendEmail() const handleClick = async () => { try { await sendEmail(userName, userEmail, userPhone) alert("邮件发送成功") } catch (err) { console.error("邮件发送失败", err) alert("邮件发送失败,请稍后重试") } } return ( <> <button onClick={handleClick} disabled={loading}> {loading ? "发送中..." : "Contact"} </button> {error && <p style={{color: 'red'}}>发送失败:{error.message}</p>} </> ) }
内容的提问来源于stack exchange,提问作者Satyam Saurabh
相关产品推荐
相关产品推荐

