React Native Web中如何为TouchableRipple实现独立Hover样式?
问题:React Native Web 中实现TouchableRipple列表项独立hover样式
在React Native Web组件中,尝试自定义TouchableRipple的交互效果,实现鼠标悬停时添加绿色边框,但当前代码中hover状态会应用到所有列表项,而非仅当前悬停的项。
现有代码:
import React, { useState } from "react" import { Pressable, View } from "react-native" import { Surface, Text, TouchableRipple } from "react-native-paper" import { colors } from "../../style/colors" interface OptionItemMenuProps { section: { title: string data: { option: string }[] }[] } export const OptionItemMenu: React.FC<OptionItemMenuProps> = ({ section }) => { const [hover, setHover] = useState(false) return ( <View> {section.map((option) => ( <View key={option.title} style={{ gap: 2 }}> <Text style={{ color: colors.grey, marginBottom: 5 }}>{option.title}</Text> {option.data.map((item) => ( <TouchableRipple key={item.option} onPress={() => console.log("teste")} onHoverIn={() => setHover(true)} onHoverOut={() => setHover(false)} style={[ { paddingVertical: 5, paddingLeft: 5, borderRadius: 5 }, hover && { borderColor: colors.primary, borderWidth: 1, paddingVertical: 4, paddingLeft: 4 }, ]} > <Text>{item.option}</Text> </TouchableRipple> ))} </View> ))} </View> ) }
问题原因
当前使用单个boolean类型的hover状态,所有列表项共用这个状态值,一旦某个项触发onHoverIn,所有项都会检测到hover为true,从而统一应用边框样式。
解决方案
方案1:使用状态存储当前悬停的项标识
将hover状态改为存储当前悬停项的唯一标识(比如item.option),而非布尔值,这样就能精准匹配到当前悬停的项:
import React, { useState } from "react" import { Pressable, View } from "react-native" import { Surface, Text, TouchableRipple } from "react-native-paper" import { colors } from "../../style/colors" interface OptionItemMenuProps { section: { title: string data: { option: string }[] }[] } export const OptionItemMenu: React.FC<OptionItemMenuProps> = ({ section }) => { // 存储当前悬停的option值,初始为null const [hoveredOption, setHoveredOption] = useState<string | null>(null) return ( <View> {section.map((option) => ( <View key={option.title} style={{ gap: 2 }}> <Text style={{ color: colors.grey, marginBottom: 5 }}>{option.title}</Text> {option.data.map((item) => ( <TouchableRipple key={item.option} onPress={() => console.log("teste")} onHoverIn={() => setHoveredOption(item.option)} onHoverOut={() => setHoveredOption(null)} style={[ { paddingVertical: 5, paddingLeft: 5, borderRadius: 5 }, // 仅当当前项的option等于hoveredOption时应用样式 item.option === hoveredOption && { borderColor: colors.primary, borderWidth: 1, paddingVertical: 4, paddingLeft: 4 }, ]} > <Text>{item.option}</Text> </TouchableRipple> ))} </View> ))} </View> ) }
方案2:抽离独立列表项组件
将每个TouchableRipple项封装成独立组件,让每个组件自己维护hover状态,彻底实现状态隔离:
import React, { useState } from "react" import { View } from "react-native" import { Text, TouchableRipple } from "react-native-paper" import { colors } from "../../style/colors" // 独立的列表项组件 const OptionItem: React.FC<{ option: string }> = ({ option }) => { const [isHovered, setIsHovered] = useState(false) return ( <TouchableRipple onPress={() => console.log("teste")} onHoverIn={() => setIsHovered(true)} onHoverOut={() => setIsHovered(false)} style={[ { paddingVertical: 5, paddingLeft: 5, borderRadius: 5 }, isHovered && { borderColor: colors.primary, borderWidth: 1, paddingVertical: 4, paddingLeft: 4 }, ]} > <Text>{option}</Text> </TouchableRipple> ) } interface OptionItemMenuProps { section: { title: string data: { option: string }[] }[] } export const OptionItemMenu: React.FC<OptionItemMenuProps> = ({ section }) => { return ( <View> {section.map((option) => ( <View key={option.title} style={{ gap: 2 }}> <Text style={{ color: colors.grey, marginBottom: 5 }}>{option.title}</Text> {option.data.map((item) => ( <OptionItem key={item.option} option={item.option} /> ))} </View> ))} </View> ) }
内容的提问来源于stack exchange,提问作者Alfredo Augusto Petri
相关产品推荐
相关产品推荐

