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

React自定义Hook执行REST请求异常:POST重复、GET未触发

React自定义Hook问题排查:POST重复调用、GET未触发

问题场景

我有一个React应用,需要完成以下流程:

  • 设置初始状态
  • 若初始对象不存在则创建新对象(POST请求)
  • 更新已创建的对象(PUT请求)
  • 获取更新后的对象(GET请求)

我用自定义Hook+本地状态实现,未使用Redux这类集中式状态管理方案,把原来单个useEffect里的代码拆成了每个REST请求对应一个自定义Hook。

主组件代码:

function MyApp() {
  const [myState, setMyState] = useState(myInitialObject);

  // 用于通过POST请求创建新对象的Hook
  const { newObject } = useCreateMyNewObject(myState);

  // 用于通过PUT请求更新已创建对象的Hook,无需返回结果
  useUpdateMyNewObject(newObject);

  // 用于调用GET方法的Hook
  const updatedObject = useFetch(newObject);

  return (
    <>       
       { 使用updatedObject的属性 }
    </>
  );
}

实际运行时,POST被调用两次(推测和Strict Mode有关),PUT调用一次,但useFetch对应的GET请求从未执行。

useFetch Hook代码:

const useFetch = (myInitialObject) => {
  const [myState, setMyState] = useState(myInitialObject);

  useEffect(() => {
    if (!myState?.id) {
      return;
    }

    async function fetchFromServer(id) {
       const fetchedObject = await fetchObject(id);
       setMyState(fetchedObject );
    }

    fetchFromServer(myState.id)

  }, [myState]);

  return myState;
};

问题分析

1. GET请求未触发的核心原因

useFetch内部依赖的是自身本地状态myState,而非传入的newObject。主组件中newObject更新时,useFetch的内部myState仅在初始化时赋值一次,后续newObject的变化不会同步到useFetch的myState中,导致useEffect的依赖项始终不满足id存在的条件,GET请求自然不会执行。

2. POST重复调用的原因

React Strict Mode在开发环境下会重复执行副作用(包括useEffect、请求逻辑等),用于检测潜在问题。如果useCreateMyNewObject里的POST请求没有做幂等处理,就会被触发两次。

修复方案

方案1:修正useFetch的监听逻辑

让useFetch监听传入的newObject,而非自身内部状态,调整后代码:

const useFetch = (targetObject) => {
  const [fetchedObject, setFetchedObject] = useState(null);

  useEffect(() => {
    // 仅当传入对象存在id时发起请求
    if (!targetObject?.id) return;

    async function fetchData() {
      const result = await fetchObject(targetObject.id);
      setFetchedObject(result);
    }

    fetchData();
  }, [targetObject]); // 依赖传入的外部对象,而非内部状态

  return fetchedObject;
};

方案2:处理POST请求的幂等性

在useCreateMyNewObject中添加请求状态标记,避免Strict Mode下重复调用:

const useCreateMyNewObject = (initialObject) => {
  const [newObject, setNewObject] = useState(null);
  const [isCreating, setIsCreating] = useState(false);

  useEffect(() => {
    // 初始对象已有id或请求正在进行时,跳过创建逻辑
    if (initialObject?.id || isCreating) return;

    async function createObject() {
      setIsCreating(true);
      const result = await createNewObject(initialObject);
      setNewObject(result);
      setIsCreating(false);
    }

    createObject();
  }, [initialObject, isCreating]);

  return { newObject };
};

方案3:调整主组件Hook依赖顺序

确保更新逻辑仅在有效对象存在时执行,避免无效调用:

function MyApp() {
  const [myState, setMyState] = useState(myInitialObject);

  const { newObject } = useCreateMyNewObject(myState);

  // 仅当newObject存在id时,执行更新操作
  useEffect(() => {
    if (newObject?.id) {
      updateMyObject(newObject);
    }
  }, [newObject]);

  const updatedObject = useFetch(newObject);

  return (
    <>       
       {updatedObject ? <div>{updatedObject.someProperty}</div> : <div>加载中...</div>}
    </>
  );
}

内容的提问来源于stack exchange,提问作者Olga Nosova

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 20:23:20