React自定义Hook useAxios在按钮点击事件调用报错如何修复
报错原因
你触发的是React官方的Hook规则校验:所有React Hook(包括自定义Hook)只能在函数组件的顶层作用域调用,不能在事件回调、循环、条件判断等非顶层位置调用。
你当前封装的useAxios默认会在组件加载时自动发起请求,不满足点击登录按钮才触发请求的需求,同时你在onFinish提交回调里调用Hook的写法本身就违反规则,所以才会报错。
修复步骤
1. 改造自定义Hook useAxios,支持手动触发请求
调整Hook逻辑,把请求方法暴露出来,同时支持配置是否自动发起请求,适配不同场景:
import { useState, useEffect, useCallback } from 'react'; import axios from 'axios'; axios.defaults.baseURL = 'http://localhost:3000/'; const useAxios = ({ url, method, auto = false, body = null, headers = null }) => { const [response, setResponse] = useState(null); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); // 把请求方法暴露出去,支持手动调用,useCallback避免不必要的重渲染 const fetchData = useCallback(async (customBody = body, customHeaders = headers) => { setLoading(true); setError(''); try { const res = await axios[method](url, customHeaders, customBody); setResponse(res.data); return res.data; } catch (err) { setError(err); throw err; } finally { setLoading(false); } }, [method, url, body, headers]); // 支持配置是否进入组件就自动发起请求,适配页面初始化拉取数据的场景 useEffect(() => { if (auto) { fetchData(); } }, [fetchData, auto]); return { response, error, loading, fetchData }; }; export default useAxios;
2. 改造LoginForm组件的调用逻辑
在组件顶层调用Hook拿到手动触发方法,提交时再调用即可:
import React from 'react'; import { Form, Input, Button, message } from 'antd'; import useAxios from '../hooks/useAxios'; const LoginForm = () => { // 组件顶层调用Hook,配置登录请求基础参数,设置auto为false不自动发请求 const { fetchData, loading } = useAxios({ url: 'api/auth/login', method: 'post', auto: false }); const onFinish = async (body) => { try { // 提交表单时手动调用请求方法,传入表单参数 const loginRes = await fetchData(body); console.log('登录成功', loginRes); message.success('登录成功'); // 后续可添加存储token、跳转页面等逻辑 } catch (err) { console.error('登录失败', err); message.error('登录失败,请检查账号密码'); } }; const findIDAndPassword = () => { console.log('findIDAndPassword'); }; return ( <Form name="basic" onFinish={onFinish} autoComplete="off"> <Form.Item name="id" rules={[ { required: true, message: '请输入账号!', }, ]} > <Input placeholder="账号" /> </Form.Item> <Form.Item name="password" rules={[ { required: true, message: '请输入密码!', }, ]} > <Input.Password placeholder="密码" /> </Form.Item> <Form.Item> <Button type="primary" htmlType="submit" block loading={loading}> 登录 </Button> </Form.Item> <Button type="link" block onClick={findIDAndPassword}> 找回账号密码 </Button> </Form> ); }; export default LoginForm;
补充说明
如果仅需要处理登录这一个单次触发的请求,不想修改现有useAxios封装,也可以直接在onFinish里直接调用axios发请求,不需要走自定义Hook,原本的useAxios更适合页面初始化就需要拉取数据的场景,比如列表页加载数据。
内容的提问来源于stack exchange,提问作者고윤태
相关产品推荐
相关产品推荐

