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同时显示,不符合预期。
解决方案
- 将带Tooltip的按钮封装成独立的React函数组件,在组件内部使用
useState管理自身的Tooltip显示状态。 - 让原
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
相关产品推荐
相关产品推荐

