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

React Native useEffect逻辑错误优化求助:fetchData提前调用问题

修复useEffect触发时机错误导致fetchData拿到null参数的问题

你的核心问题在于useEffect的依赖配置错误,加上React状态更新的异步特性,导致fetchData在apiToken和userId还没拿到有效值时就被调用了。下面是具体的问题分析和优化后的代码:

问题根源

  1. 组件初始渲染时,apiToken和userId都是null,此时useEffect因为依赖这两个值会立即触发,调用getData后立刻通过.then()执行fetchData——但setApiToken和setUserId是异步操作,状态还没更新,所以fetchData里拿到的还是初始的null值。
  2. 当getData完成并更新apiToken和userId后,useEffect又会因为依赖变化再次触发,导致getData被重复调用,陷入不必要的循环。

优化后的代码

export const SitesScreen = ({navigation}) => {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [isRefreshing, setIsRefreshing] = useState(false);
  const [apiToken, setApiToken] = useState(null);
  const [userId, setUserId] = useState(null);

  // 重构fetchData:添加参数校验,统一用async/await写法,增加错误捕获
  const fetchData = async (token, id) => {
    // 先校验参数是否有效,无效直接返回
    if (!token || !id) {
      setLoading(false);
      setIsRefreshing(false);
      return;
    }

    setIsRefreshing(true);
    try {
      const response = await fetch(
        `${global.endpoint}/sites/current-user-sites/${id}?access-token=${token}`,
        { method: 'GET' }
      );
      const responseJson = await response.json();
      
      if (responseJson.success) {
        setData(responseJson.data);
        console.log(responseJson);
      }
    } catch (error) {
      console.error('请求失败:', error);
      // 这里可以添加错误提示逻辑,比如Toast提示用户
    } finally {
      setLoading(false);
      setIsRefreshing(false);
    }
  };

  const getData = async () => {
    try {
      const deviceDataStr = await AsyncStorage.getItem('device_data');
      const deviceData = JSON.parse(deviceDataStr);
      const { access_token: token, id } = deviceData;
      
      setApiToken(token);
      setUserId(id);
      // 直接把拿到的token和id传给fetchData,不用等状态更新
      await fetchData(token, id);
    } catch (error) {
      console.error('获取本地数据失败:', error);
      setLoading(false);
    }
  };

  // 只在组件挂载时执行一次getData,不需要依赖apiToken和userId
  useEffect(() => {
    getData();
  }, []);

  const renderItem = ({
    item,
    index,
  }: {
    item: IListItem,
    index: number,
  }): React.ReactElement => (
    <ListItem
      title={`${item.name}`}
      description={`${item.address}, ${item.number} - ${item.city}`}
      onPress={() =>
        navigation.navigate('Details', {
          siteId: item.id,
        })
      }
    />
  );

  // 下拉刷新时,用已有的状态值调用fetchData
  const handleRefresh = () => {
    fetchData(apiToken, userId);
  };

  return (
    <SafeAreaView style={GlobalStyles.safeArea}>
      <TopNavigation
        title={evaProps => (
          <Text style={GlobalStyles.headerTitle}>Cantieri</Text>
        )}
        alignment="center"
      />
      <Divider />
      <FlatList
        data={data}
        refreshControl={
          <RefreshControl refreshing={isRefreshing} onRefresh={handleRefresh} />
        }
        ItemSeparatorComponent={Divider}
        renderItem={renderItem}
      />
    </SafeAreaView>
  );
};

关键改动说明

  1. 调整useEffect触发时机:把依赖数组改为空数组[],让getData只在组件首次挂载时执行一次,避免重复调用和循环触发。
  2. fetchData参数直接传递:在getData拿到deviceData后,直接把token和id传给fetchData,绕开React状态更新的异步延迟,确保请求拿到的是有效值。
  3. 添加参数校验:在fetchData开头先检查token和id是否存在,防止无效请求。
  4. 统一异步写法:把原来混合的async/await和.then()改成纯async/await,代码更易读,同时添加try/catch捕获请求和解析错误,避免程序崩溃。
  5. 独立下拉刷新逻辑:单独写handleRefresh函数,下拉时用已保存的apiToken和userId调用fetchData,确保刷新时参数有效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 18:47:02