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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 01:45:27