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

React Native添加Tooltip后出现Render Error及useState异常求助

问题根源

这个错误是因为你在普通工具函数header中直接调用了React Hook(useState),违反了React Hooks的核心规则:Hooks只能在React函数组件或自定义Hook的顶层调用。React Navigation的header配置函数会被框架多次执行,这种场景下使用Hook会导致React的状态管理逻辑混乱,触发"Should have a queue"这类底层错误。

另外代码还存在逻辑问题:所有带tooltip的按钮共用同一个isTooltipVisible状态,点击任意一个按钮会让所有Tooltip同时显示,不符合预期。

解决方案
  1. 将带Tooltip的按钮封装成独立的React函数组件,在组件内部使用useState管理自身的Tooltip显示状态。
  2. 让原header函数只负责返回导航配置,不再包含Hook调用。
修改后的完整代码
import React, { useState } from 'react';
import { GestureResponderEvent, Text, TouchableOpacity, View } from 'react-native';
import Icon from 'react-native-vector-icons/Feather';
import Icon1 from 'react-native-vector-icons/AntDesign';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { typography } from '../../utils';
import { HomeStackParamList } from './home';
import Header from '@components/header';
import Tooltip from 'react-native-walkthrough-tooltip';

export interface HeaderProps {
  rightItems?: {
    icon: string;
    iconSet: 'Feather' | 'AntDesign';
    onPress?: (event: GestureResponderEvent) => void;
    tooltipText?: string;
  }[];
  withSearch?: boolean;
  alternateTitle?: string;
}

// 封装带Tooltip的按钮组件
const TooltipButton = ({ icon, iconSet, tooltipText }: {
  icon: string;
  iconSet: 'Feather' | 'AntDesign';
  tooltipText: string;
}) => {
  // 每个TooltipButton拥有独立的显示状态
  const [isTooltipVisible, setIsTooltipVisible] = useState(false);

  const renderIcon = () => {
    if (iconSet === 'Feather') {
      return <Icon name={icon} size={24} />;
    }
    return <Icon1 name={icon} size={24} />;
  };

  return (
    <Tooltip 
      isVisible={isTooltipVisible} 
      content={<Text>{tooltipText}</Text>} 
      backgroundColor="gray"
      onClose={() => setIsTooltipVisible(false)} // 添加关闭逻辑
    >
      <TouchableOpacity onPress={() => setIsTooltipVisible(true)}>
        {renderIcon()}
      </TouchableOpacity>
    </Tooltip>
  );
};

// 普通按钮组件(不带Tooltip)
const RegularButton = ({ icon, iconSet, onPress }: {
  icon: string;
  iconSet: 'Feather' | 'AntDesign';
  onPress?: (event: GestureResponderEvent) => void;
}) => {
  const renderIcon = () => {
    if (iconSet === 'Feather') {
      return <Icon name={icon} size={24} />;
    }
    return <Icon1 name={icon} size={24} />;
  };

  return (
    <TouchableOpacity onPress={onPress}>
      {renderIcon()}
    </TouchableOpacity>
  );
};

const header = (
  stackScreenProps: NativeStackScreenProps<HomeStackParamList>,
  headerProps: HeaderProps
) => {
  const { route, navigation } = stackScreenProps;
  const { rightItems, withSearch } = headerProps;

  if (withSearch !== undefined)
    return { header: () => <Header {...{ withSearch }} /> };
  return {
    headerTitle: () => (
      <Text style={typography.heading2}>
        {headerProps.alternateTitle
          ? headerProps.alternateTitle
          : route.name.split('_').join(' ')}
      </Text>
    ),
    headerLeft: () => (
      <TouchableOpacity onPress={() => navigation.goBack()}>
        <Icon name="chevron-left" size={24} />
      </TouchableOpacity>
    ),
    headerRight: () =>
      rightItems?.map(({ icon, iconSet, onPress, tooltipText }) => (
        <View style={{ marginLeft: 20 }} key={`${icon}-${iconSet}`}>
          {tooltipText ? (
            <TooltipButton icon={icon} iconSet={iconSet} tooltipText={tooltipText} />
          ) : (
            <RegularButton icon={icon} iconSet={iconSet} onPress={onPress} />
          )}
        </View>
      )),
  };
};

export default header;
额外优化点
  • 给Tooltip添加了onClose回调,点击Tooltip外部时自动关闭弹窗,提升用户体验。
  • 将图标渲染逻辑抽成复用函数,减少代码重复。
  • 拆分了Tooltip按钮和普通按钮,职责更清晰。

内容的提问来源于stack exchange,提问作者Yong Jie Lau

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 12:05:06