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

React Native页面Hook重复触发API调用问题排查与解决

解决React Native页面重复调用API及Hook重复执行问题

问题根源

  1. useCheckVersion Hook中,当apiVersion更新时会执行异步逻辑,若调用setNewVersion更新状态,会触发组件重渲染;
  2. useGetGrandPrix虽依赖空数组,但因未做防重复请求处理,加上组件多次重渲染的连锁反应,导致API重复调用;
  3. useCheckVersion内AsyncStorage.getItem的回调写法错误,可能引发逻辑异常,导致不必要的状态更新。

解决方案

1. 给useGetGrandPrix添加防重复请求逻辑

通过useRef标记请求状态,确保API仅调用一次:

import { useState, useEffect, useRef } from 'react';
import { getToken } from '../../../services/common/auth/storage';
import { getGranPrix } from '../../../services/common/api/circuits';

export const useGetGrandPrix = () => {
  const [data, setData] = useState({});
  const [apiVersion, setApiVersion] = useState('');
  const [loading, setLoading] = useState(true);
  const [errorMessage, setErrorMessage] = useState('');
  const hasFetched = useRef(false); // 标记是否已完成请求

  useEffect(() => {
    const getData = async () => {
      if (hasFetched.current) return; // 已请求过则直接返回
      hasFetched.current = true;

      try {
        const token = await getToken();
        const response = await getGranPrix(token);

        if (!response.ok) {
          setErrorMessage('Could not retrieve data.');
        } else {
          setData(response.data.list);
          setApiVersion(response.data.version);
        }
      } catch (err) {
        setErrorMessage('Could not retrieve data.');
      } finally {
        setLoading(false);
      }
    };
    getData();
  }, []);

  return { data, apiVersion, loading, errorMessage };
};

2. 优化useCheckVersion逻辑

  • 修复AsyncStorage的错误用法,移除多余回调;
  • 仅在状态实际变化时更新,避免无意义重渲染:
import { useState, useEffect } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { APP } from '../../../utils/constants/app';

export const useCheckVersion = (apiVersion) => {
  const [isNewVersion, setNewVersion] = useState(false);

  useEffect(() => {
    const getVersion = async () => {
      const checkVersion = () => {
        if (apiVersion === APP.VERSION) return false;
        return apiVersion > APP.VERSION;
      };
      const versionNeedUpdate = checkVersion(apiVersion);

      let versionPrompt = {
        dateLastPrompt: null,
        dateNextPrompt: null,
      };

      const currentWeekday = new Date().getUTCDay();
      const currentPromptUTC = new Date().toISOString();
      const nextPromptUTC = new Date(currentPromptUTC);
      nextPromptUTC.setDate(nextPromptUTC.getDate() + 7);
      nextPromptUTC.setHours(0, 0, 0, 0);

      if (!versionNeedUpdate) {
        const storageItem = await AsyncStorage.getItem('versionPrompt');
        if (storageItem) {
          await AsyncStorage.removeItem('versionPrompt');
        }
        // 若当前不需要更新且状态为true,才更新状态
        if (isNewVersion) {
          setNewVersion(false);
        }
        return;
      }

      // 需要更新的逻辑
      const storageItemString = await AsyncStorage.getItem('versionPrompt');
      const storageItem = storageItemString ? JSON.parse(storageItemString) : null;

      if (!storageItem) {
        versionPrompt = {
          dateLastPrompt: currentPromptUTC,
          dateNextPrompt: nextPromptUTC,
        };
        await AsyncStorage.setItem('versionPrompt', JSON.stringify(versionPrompt));
        if (!isNewVersion) {
          setNewVersion(true);
        }
      } else {
        const dateNextPrompt = new Date(storageItem.dateNextPrompt);
        const currentDate = new Date();

        if (dateNextPrompt < currentDate && currentWeekday > 2) {
          versionPrompt = {
            dateLastPrompt: currentPromptUTC,
            dateNextPrompt: nextPromptUTC,
          };
          await AsyncStorage.setItem('versionPrompt', JSON.stringify(versionPrompt));
          if (!isNewVersion) {
            setNewVersion(true);
          }
        } else {
          // 不需要提示更新时,若状态为true则重置
          if (isNewVersion) {
            setNewVersion(false);
          }
        }
      }
    };
    getVersion();
  }, [apiVersion, isNewVersion]);

  return { isNewVersion };
};

3. 修复Screen组件的导航参数问题

确保navigation从props中解构,避免报错引发的异常渲染:

import React from 'react';
import { FlatList, Text } from 'react-native';
import { useGetGrandPrix } from '../hooks/api';
import { useCheckVersion } from '../hooks/system';
import GrandPrix from './GrandPrix'; // 需确保自定义组件已导入

function GrandPrixScreen(props) {
  const { navigation } = props; // 解构navigation
  const { data, apiVersion, loading, errorMessage } = useGetGrandPrix();
  const { isNewVersion } = useCheckVersion(apiVersion);

  if (loading) {
    return <Text>Loading...</Text>;
  }

  if (errorMessage) {
    return <Text>{errorMessage}</Text>;
  }

  if (isNewVersion) {
    return <Text>Please update app to latest version.</Text>;
  }

  return (
    <FlatList
      data={data}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => (
        <GrandPrix
          name={item.name}
          length={item.length}
          onPress={() =>
            navigation.navigate('GrandPrixDetailsScreen', { item })
          }
        />
      )}
    />
  );
}

export default GrandPrixScreen;

4. 排除React严格模式影响(开发环境)

若处于开发环境,React严格模式会让useEffect执行两次(生产环境不会)。可检查根组件是否包裹<StrictMode>,若需避免开发环境的重复请求,可临时移除,但生产环境无需处理。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 15:55:07