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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 23:15:55