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

React Native嵌套导航导致组件初始化时API被调用两次的解决方法

解决React Native嵌套导航中组件重复渲染导致API重复调用的问题

问题原因

你的代码中DiscoverScreen同时被注册为TabNavigator的直接页面和StackNavigator的子页面,这会生成两个完全独立的组件实例。无论你是切换到Discover标签,还是从Home栈导航进入Discover,两个实例都会各自执行useEffect,最终导致API被调用两次。


解决方案

方案一:统一导航结构(推荐)

从根源上避免组件重复注册,调整导航层级,让DiscoverScreen只属于一个导航容器。可以给每个Tab都配置独立的StackNavigator:

// 定义Home标签对应的栈导航
const HomeStack = () => {
  return (
    <Stack.Navigator initialRouteName="HomeScreen">
      <Stack.Screen
        name="HomeScreen"
        component={HomeScreen}
        options={{ headerShown: false }}
      />
      <Stack.Screen
        name="Planty"
        component={PlantyScreen}
        options={{ headerShown: false }}
      />
      <Stack.Screen
        name="Specific"
        component={SpecificPlantScreen}
        options={{ headerShown: false }}
      />
    </Stack.Navigator>
  );
};

// 定义Discover标签对应的栈导航
const DiscoverStack = () => {
  return (
    <Stack.Navigator initialRouteName="Discover">
      <Stack.Screen
        name="Discover"
        component={DiscoverScreen}
        options={{ headerShown: false }}
      />
      {/* 可添加Discover页面需要跳转的其他页面 */}
    </Stack.Navigator>
  );
};

// 定义MyGarden标签对应的栈导航
const MyGardenStack = () => {
  return (
    <Stack.Navigator initialRouteName="MyGarden">
      <Stack.Screen
        name="MyGarden"
        component={MyGardenScreen}
        options={{ headerShown: false }}
      />
    </Stack.Navigator>
  );
};

// 外层Tab导航
const TabNavigator = () => {
  return (
    <Tab.Navigator
      initialRouteName="Home"
      screenOptions={() => ({
        tabBarStyle: {
          height: 80,
          backgroundColor: "#232516",
          paddingBottom: 5,
        },
        headerShown: false,
        tabBarActiveTintColor: "white",
        tabBarInactiveTintColor: "gray",
      })}
    >
      <Tab.Screen
        name="Home"
        component={HomeStack}
        options={{
          tabBarIcon: ({ focused }) => (
            <FontAwesome
              style={focused ? styles.focusedNavButtonBackground : {}}
              name="home"
              size={35}
              color={focused ? "black" : "white"}
            />
          ),
        }}
      />
      <Tab.Screen
        name="Discover"
        component={DiscoverStack}
        options={{
          tabBarIcon: ({ focused }) => (
            <FontAwesome
              style={focused ? styles.focusedNavButtonBackground : {}}
              name="search"
              size={35}
              color={focused ? "black" : "white"}
            />
          ),
        }}
      />
      <Tab.Screen
        name="MyGarden"
        component={MyGardenStack}
        options={{
          tabBarIcon: ({ focused }) => (
            <FontAwesome6
              style={focused ? styles.focusedNavButtonBackground : {}}
              name="sun-plant-wilt"
              size={35}
              color={focused ? "black" : "white"}
            />
          ),
        }}
      />
    </Tab.Navigator>
  );
};

如果需要从Home页面跳转到Discover,直接调用navigation.navigate('Discover')即可,这样会切换到Tab的Discover标签,使用同一个组件实例。


方案二:在API层添加缓存逻辑

如果暂时不想调整导航结构,可以在usePlants Hook中添加请求缓存,避免相同参数的重复调用:

const usePlants = () => {
  const [plants, setPlants] = useState([]);
  const [errorMessage, setErrorMessage] = useState('');
  const [page, setPage] = useState(1);
  const lastRequestedPage = useRef(null);

  const getPlants = async (currentPage) => {
    // 跳过重复的page请求
    if (lastRequestedPage.current === currentPage) return;
    
    try {
      lastRequestedPage.current = currentPage;
      // 替换为你的实际API地址
      const response = await fetch(`your-api-endpoint?page=${currentPage}`);
      const data = await response.json();
      setPlants(prev => [...prev, ...data]);
      setErrorMessage('');
    } catch (err) {
      setErrorMessage('获取植物列表失败');
      // 请求失败时重置缓存标记,允许重新请求
      lastRequestedPage.current = null;
    }
  };

  const incrementPageNumber = () => {
    setPage(prev => prev + 1);
  };

  useEffect(() => {
    getPlants(page);
  }, [page]);

  return [getPlants, incrementPageNumber, page, plants, errorMessage];
};

这样即使存在两个DiscoverScreen实例,当它们请求相同page的数据时,只会触发一次API调用。


方案三:统一页面跳转入口

如果希望从Home栈跳转到Discover时直接切换到Tab标签,而非在栈内创建新实例,修改Home页面的跳转逻辑:

// 原栈内跳转逻辑(会创建新实例)
// navigation.navigate('Discover');

// 修改为直接跳转到Tab的Discover标签
navigation.navigate('Discover');

这样所有进入Discover的操作都会复用Tab中的组件实例,避免重复渲染。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 04:57:34