Invalid hook call报错:函数组件不改为类组件如何正常调用异步函数
问题原因定位
首先可以明确,Invalid Hook Call报错确实可能是在函数组件体之外、或异步流程/事件回调中调用Hook触发的。React Hook官方规则明确要求Hook只能在函数组件的最顶层、或自定义Hook的最顶层调用,不允许在循环、条件判断、异步函数、普通工具函数中调用,完全不需要将函数组件改为类组件即可解决。
你当前报错的核心诱因大概率来自以下几个可排查点:
- 你封装的
PasswordApi类的get方法中调用了React Hook:在异步请求流程中调用类方法,等于在组件顶层之外的异步上下文调用Hook,直接违反Hook规则 - 上下文
verifyCodeContext的使用不符合规范:如果你是在getVerificationCode异步函数中调用了useContext获取上下文,也会触发规则报错;直接修改context属性的写法本身也不符合React数据流规范 - 异步请求没有异常捕获:请求报错后的异常流程可能触发未预料的Hook调用
可行解决方案
1. 排查并修正Api类的Hook使用
如果PasswordApi的底层get方法中用到了useAxios/自定义请求Hook,立刻将Hook的调用逻辑移到函数组件顶层,不要在类方法中使用任何React Hook。如果需要复用请求逻辑,可以将请求逻辑抽为自定义Hook,在组件顶层调用后拿到请求触发函数,再在onSubmit事件中调用触发函数即可。
2. 修正Context使用逻辑
所有Hook(包括useContext)必须在组件最顶层调用,不要在异步函数中调用useContext。同时不要直接修改Context对象的属性,要通过Context对外暴露的更新方法修改数据,避免不触发组件渲染的问题。
3. 补充异步逻辑的异常捕获
给异步请求代码包裹try/catch,避免请求异常溢出导致不可控的代码执行逻辑。
修改后的参考代码
import { useContext } from 'react'; export default function SendVerificationCode({ route, navigation }: NativeStackScreenProps< NativeStackParameterList, 'SendVerificationCode' >): JSX.Element { // 组件顶层调用useContext获取上下文 const verifyCodeContext = useContext(VerifyCodeContext); // 组件顶层初始化Api实例,不要在事件回调中重复初始化 const passwordApi = new PasswordApi(); async function getVerificationCode( agencyKey: number, username: string, email: string, verificationCode: string ): Promise<void> { try { const response = await passwordApi.getReset( new GetResetRequest({ agencyKey: agencyKey, email: email, username: username, verificationCode: verificationCode }) ); // 使用上下文暴露的更新方法修改数据,不要直接修改属性 verifyCodeContext.setUserName(username); navigation.navigate('VerifyCode', { copyrightYear: 2021, version: '0.0.0.1' }); } catch (err) { // 自行补充请求失败的交互提示 console.error('验证码发送失败', err); } } const onSubmit = (data: any) => { getVerificationCode( 1234, data.username, data.email, '123' ); }; return ( <UnauthenticatedTemplate copyrightYear={year} version={version}> <Form style={styles.form} submitText="Send Code" onSubmit={onSubmit} schema={SendVerificationCodeSchema} > <FCTextField name="username" placeholder="Username" /> <FCTextField name="email" placeholder="Email" /> </Form> </UnauthenticatedTemplate> ); }
内容的提问来源于stack exchange,提问作者Ackman
相关产品推荐
相关产品推荐

