React中如何在onClick事件内正确使用自定义useFetch Hook?
如何在按钮点击时触发自定义useFetch Hook的请求?
我是编程专业学生,正在开发登录/注册页面,写了一个自定义useFetch Hook用来获取用户数据或提交登录/注册数据。但React规定自定义Hook只能在React组件或其他自定义Hook内部调用,我想在按钮点击(onClick)时触发登录/注册请求,请问最佳实现方式是什么?
我的自定义useFetch Hook代码:
import React, { useState, useEffect } from "react"; import axios from "axios"; import { useHistory } from "react-router-dom"; import { toast } from "react-toastify"; const useFetch = () => { const [data, setData] = useState([]); const history = useHistory(); let initialBizCardArray = []; useEffect(() => { (async () => { try { let currLocation = history.location.pathname; switch (currLocation) { case (currLocation = "/my-cards"): case (currLocation = "/cards"): { let { data } = await axios.get(`/cards${currLocation}`); initialBizCardArray = data; setData(initialBizCardArray); } break; case (currLocation = "/login"): case (currLocation = "/register"): { let { data } = await axios.post(`/users${currLocation}`); initialBizCardArray = data; setData(initialBizCardArray); } break; } } catch (error) { toast.error(error, { position: "top-right", autoClose: 2000, hideProgressBar: false, closeOnClick: true, pauseOnHover: true, draggable: true, progress: undefined, }); return { error }; } })(); }, []); return { data, setData, initialBizCardArray }; }; export default useFetch;
我尝试在登录提交函数中调用useFetch的方式:
const handleSubmitLogIn = async (ev) => { ev.preventDefault(); ValidateErr( { email: loginInput.email, password: loginInput.password, }, loginSchema ); try { let { data } = await useFetch(userLocation, { email: loginInput.email, password: loginInput.password, }); console.log("Succuss"); localStorage.setItem("token", data.token); autoLoginFunction(data.token); setTimeout(() => { let userInfo = jwt_decode(data.token); userInfo && userInfo.biz ? history.push("/my-cards") : history.push("/"); }, 100); } catch (err) { console.error("error", err.response.data); toast.error(`😭 Email or password are invalid.`, err, { position: "top-right", autoClose: 2000, hideProgressBar: false, progress: undefined, }); history.push("/login"); }
解决方案
问题核心
你当前的useFetch Hook设计不符合需求:它在组件挂载时自动执行请求,且硬编码了路径判断,无法接收外部参数控制请求时机;直接在事件函数中调用Hook违反了React的Hook规则,必然报错。
重构后的useFetch Hook
重新设计Hook,让它返回可手动触发的请求函数,而非自动执行请求:
import { useState } from "react"; import axios from "axios"; import { toast } from "react-toastify"; const useFetch = () => { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); // 通用请求函数,支持自定义请求方法、地址、参数 const fetchData = async (method, url, payload = null) => { setLoading(true); setError(null); try { const response = await axios({ method, url, data: payload }); setData(response.data); return response.data; } catch (err) { const errorMsg = err.response?.data?.message || err.message; toast.error(errorMsg, { position: "top-right", autoClose: 2000, hideProgressBar: false, closeOnClick: true, pauseOnHover: true, draggable: true }); setError(errorMsg); throw err; // 抛出错误让调用方处理后续逻辑 } finally { setLoading(false); } }; return { data, loading, error, fetchData }; }; export default useFetch;
在组件中正确使用
在组件顶层调用useFetch,然后在点击事件中调用返回的fetchData函数:
import useFetch from './useFetch'; import { useHistory } from 'react-router-dom'; import jwt_decode from 'jwt-decode'; import { toast } from 'react-toastify'; const Login = () => { const history = useHistory(); const { fetchData, loading } = useFetch(); const [loginInput, setLoginInput] = useState({ email: '', password: '' }); const handleSubmitLogIn = async (ev) => { ev.preventDefault(); // 表单验证逻辑 ValidateErr( { email: loginInput.email, password: loginInput.password }, loginSchema ); try { // 调用fetchData发起登录请求 const data = await fetchData('POST', '/users/login', { email: loginInput.email, password: loginInput.password }); localStorage.setItem("token", data.token); autoLoginFunction(data.token); const userInfo = jwt_decode(data.token); userInfo?.biz ? history.push("/my-cards") : history.push("/"); } catch (err) { console.error("登录失败", err.response?.data); toast.error("邮箱或密码无效", { position: "top-right", autoClose: 2000, hideProgressBar: false }); } }; // 其他组件逻辑... };
关键改进点
- 移除自动执行的useEffect,改为返回手动触发的
fetchData函数,完全由调用方控制请求时机 - 新增
loading和error状态,方便组件处理加载态和错误提示 - 支持自定义请求方法、地址、参数,复用性更强,可适配登录、注册、数据获取等多种场景
- 严格遵守React Hook规则:在组件顶层调用useFetch,事件函数中仅调用返回的普通函数
内容的提问来源于stack exchange,提问作者Reef Goldberg
相关产品推荐
相关产品推荐

