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

React Navigation顶部Tab导航指示器样式指向错误修复问询

问题:Tab导航指示器始终指向上一个Tab而非当前选中项

我用React Navigation的createMaterialTopTabNavigator实现了Tab页面导航,导航逻辑和自定义tabBarLabel样式都正常,但tabBarIndicatorStyle始终应用在上一个Tab,不是当前选中的Tab,请问怎么修复?

代码示例

import AsyncStorage from '@react-native-async-storage/async-storage';
import {createMaterialTopTabNavigator} from '@react-navigation/material-top-tabs';
import {useIsFocused} from '@react-navigation/native';
import axios from 'axios';
import React, {useEffect, useState} from 'react';
import {ActivityIndicator, Text, View} from 'react-native';
import {Constants} from '../config/constants';
import {getAdvancedSearchAduanaService} from '../services/advancedSearch.service';
import Cards from './Cards';
import styles from './Style';

const CardList = ({route}) => {
  const {screenProps} = route.params;

  const [data, setData] = useState([]);
  const [action, setAction] = useState({});
  const [config, setConfig] = useState([]);
  const [advancedSearch, setAdvancedSearch] = useState([]);
  const [collapseConfig, setCollapseConfig] = useState([]);
  const [pag, setPag] = useState(1);
  const [totalPag, setTotalPag] = useState(1);
  const [limit, setLimit] = useState(10);
  const [totalData, setTotalData] = useState(0);
  const [search, setSearch] = useState('');
  const [loadData, setLoadData] = useState(false);
  const [clientsSelected, setClientsSelected] = useState('[]');
  const [advancedObj, setAdvancedObj] = useState([]);

  const isFocused = useIsFocused();

  useEffect(() => {
    if (isFocused) {
      console.log(`Tab ${route.name} está activo`);
    }
  }, [isFocused]);

  const setAdvanced = (advancedSearch) => {
    let obj = [];
    const newData = JSON.parse(JSON.stringify(advancedSearch));

    newData.map((e) => {
      e.values.map((f) => {
        let objPush = {
          id: f.id,
          isDate: f.type === 'dateRange' ? true : false,
          value:
            f.type === 'dateRange'
              ? f.value && f.value2
                ? {
                    init: f.value,
                    end: f.value2,
                  }
                : ''
              : f.value
              ? f.value
              : '',
        };
        obj.push(objPush);
      });
    });
    setAdvancedObj(obj);
    setPag(1);

    return obj;
  };

  const initializeSearchedData = async () => {
    const dataSearched = await getAdvancedSearchAduanaService();

    return dataSearched?.origin && dataSearched?.origin === route?.name
      ? setAdvanced(dataSearched.advancedSearch)
      : dataSearched?.advancedSearch;
  };

  const getCatalogs = async () => {
    setLoadData(false);
    // const routeName = route.name;
    const selectedClients = await AsyncStorage.getItem('clientsSelected');
    if (selectedClients) {
      try {
        setClientsSelected(selectedClients);
        await requestData(selectedClients);
      } catch (error) {
        console.log('error Integrada', error.response);
        setClientsSelected(selectedClients);
        await requestData(selectedClients);
      } finally {
        setLoadData(true);
      }
    }
  };

  const requestData = async (res) => {
    const dataSearched = await initializeSearchedData();

    const obj = {
      search,
      limit,
      pag,
      client: res,
      EsDTA: route.name === 'Impo' ? 0 : 1,
      advanced: dataSearched ?? advancedObj,
    };
    let url = `${Constants.API_TRAZABILIDAD}customs/listCustomImports`;
    if (route.name === 'Expo') {
      url = `${Constants.API_TRAZABILIDAD}customs/listCustomExport`;
    }

    try {
      const response = await axios.post(url, obj);
      const newTotal = response.data.data[0]
        ? Number(response.data.data[0].Total)
        : 0;
      const newAllData = response.data.data.map((item) => ({
        ...item,
        image: 2,
      }));
      setData(res === undefined ? [] : newAllData);
      setTotalData(newTotal);
      setTotalPag(Math.ceil(newTotal / limit));
    } catch (error) {
      console.log('error Integrada', error.response);
      setData([]);
      setTotalData(0);
      setTotalPag(0);
    }
  };

  const loadMore = async () => {
    let newPag = pag + 1;
    let obj = {
      search,
      limit,
      pag: newPag,
      client: clientsSelected,
      EsDTA: route === 'Impo' ? 0 : 1,
      advanced: advancedObj,
    };
    setPag(newPag);
    await requestChange(obj);
  };

  const requestChange = async (obj) => {
    let url = `${Constants.API_TRAZABILIDAD}customs/listCustomImports`;
    if (route.name === 'Expo') {
      url = `${Constants.API_TRAZABILIDAD}customs/listCustomExport`;
    }
    axios
      .post(url, obj)
      .then((response) => {
        let newAllData = data.concat(response.data.data);
        newAllData.map((e) => {
          e.image = 2;
        });
        setData(newAllData);
        setLoadData(true);
      })
      .catch((error) => {
        setData([]);
        setLoadData(true);
        console.log('error Integrada', error.response);
      });
  };

  useEffect(() => {
    const {action, config, collapseConfig, data} = screenProps;
    switch (route.name) {
      case 'Impo':
        setData(data);
        setAdvancedSearch(screenProps.advancedSearch);
        setAction(action);
        setConfig(config);
        setCollapseConfig(collapseConfig);
        break;
      case 'DTAs':
        setData(screenProps.dataDta);
        setAdvancedSearch(screenProps.advancedSearchDta);
        setAction(screenProps.actionDta);
        setConfig(screenProps.configDta);
        setCollapseConfig(screenProps.collapseConfigDta);
        break;
      default:
        setData(screenProps.dataExpo);
        setAdvancedSearch(screenProps.advancedSearchExpo);
        setAction(screenProps.actionExpo);
        setConfig(screenProps.configExpo);
        setCollapseConfig(screenProps.collapseConfigExpo);
    }
    getCatalogs();
  }, []);

  return (
    <View style={styles.bodyTabInternational}>
      {loadData ? (
        <Cards
          data={data}
          config={config}
          collapseConfig={collapseConfig}
          action={action}
          navigation={screenProps.navigation}
          mapData={screenProps.mapData}
          mapCard={screenProps.mapCard}
          mapCardData={screenProps.mapCardData}
          charts={screenProps.charts}
          map={true}
          loadMore={() => loadMore()}
          advanced={true}
          graphs={true}
          advancedSearch={advancedSearch}
          routeName={route.name}
          route="Customs"
          setSearch={setSearch}
        />
      ) : (
        <View style={styles.containerLogin}>
          <ActivityIndicator size="large" color="#003F70" />
        </View>
      )}
    </View>
  );
};

const Tab = createMaterialTopTabNavigator();

const MyApp = ({screenProps}) => {
  const isFocused = useIsFocused();

  useEffect(() => {
    if (isFocused) {
      console.log(`Tab  está activo`);
    }
  }, [isFocused]);

  return (
    <Tab.Navigator
      screenOptions={({route}) => ({
        tabBarLabel: () => {
          let title;
          if (route.name === 'Impo') title = 'Importaciones';
          else if (route.name === 'DTAs') title = 'DTAs';
          else title = 'Exportaciones';
          return <Text style={styles.focusedStyleList}>{title}</Text>;
        },
        tabBarIndicatorStyle: {
          backgroundColor: 'red',
        },
      })}>
      <Tab.Screen
        name="Impo"
        component={CardList}
        initialParams={{screenProps}}
      />
      <Tab.Screen
        name="DTAs"
        component={CardList}
        initialParams={{screenProps}}
      />
      <Tab.Screen
        name="Expo"
        component={CardList}
        initialParams={{screenProps}}
      />
    </Tab.Navigator>
  );
};

export default MyApp;

修复方案

1. 利用focused参数优化screenOptions

当前你的screenOptions只接收了route,没有利用focused状态判断Tab是否选中。修改screenOptions的参数,加入focused,同时确保指示器样式的逻辑正确:

<Tab.Navigator
  screenOptions={({ route, focused }) => ({
    tabBarLabel: () => {
      let title;
      if (route.name === 'Impo') title = 'Importaciones';
      else if (route.name === 'DTAs') title = 'DTAs';
      else title = 'Exportaciones';
      // 可选:根据focused动态切换label样式
      return <Text style={focused ? styles.focusedStyleList : styles.unfocusedStyleList}>{title}</Text>;
    },
    tabBarIndicatorStyle: {
      backgroundColor: 'red',
      // 若指示器宽度异常,可手动指定宽度适配Tab数量
      width: '33.33%'
    },
  })}
>

2. 排查样式冲突

检查styles.bodyTabInternational或全局样式是否对Tab导航器的布局造成干扰,比如设置了padding、margin或position属性导致指示器偏移。可以暂时移除自定义样式,确认问题是否消失。

3. 升级React Navigation依赖

旧版本的@react-navigation/material-top-tabs可能存在指示器定位bug,执行以下命令升级到最新稳定版:

npm install @react-navigation/material-top-tabs@latest @react-navigation/native@latest

4. 优化组件重渲染逻辑

CardList组件中的空依赖useEffect调用getCatalogs可能导致初始化时的异常状态,确保useEffect的依赖项正确,避免不必要的状态更新影响Tab导航器的选中状态。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 23:55:55