如何为数组中每个Icon组件添加独立的Hover样式?
问题描述
我想把Icon组件传入其他组件,给每个Icon添加hover效果,但遇到了问题:要么hover效果不生效,要么所有Icon同时触发hover,而不是当前被hover的单个Icon。
现有代码
CollapseFilter 组件
import { Accordion } from './Accordion'; import { filterData } from '../../../../Shared/BazaData/filterData'; export const CollapseFilter = () => { const data = filterData.name.map((item, index) => { const Icon = filterData.icons[index] return ( <> <div className='accordion' > <Accordion icon={<Icon/>} data={item.name} /> </div> </> ) }) return ( <> <div> <div> {data} </div> </div> </> ) }
Accordion 组件
import React, { useState } from 'react'; import { Down } from "../../../../Shared/icons/Down" export const Accordion = ({ data, icon }) => { const [isOpen, setIsOpen] = useState(false) const [hover, setHover] = useState(false); const [mouseover, setMouseover] = useState(false) const ChangeDownIconColor = { transform: isOpen ? 'rotate(180deg)' : '', transition: 'transform 180ms ease', fill: isOpen ? '#663FD7' : mouseover ? '#663FD7' : '' } const ChangeIconColor = { fill: isOpen ? '#663FD7' : mouseover ? '#663FD7' : '' } const ChangeDataTextColor = { color: isOpen ? '#663FD7' : mouseover ? '#663FD7' : '' } return ( <> <div className="accordion_container" onMouseOver={() => setMouseover(true)} onMouseLeave={() => setMouseover(false)} onClick={() => setIsOpen(!isOpen)}> <button className="button_container" > <div style={ChangeIconColor}>{icon}</div> <div className="title" style={ChangeDataTextColor} >{data}</div> </button> <Down style={ChangeDownIconColor}/> </div> </> ) }
解决方案
核心问题分析
- 你把渲染完成的
<Icon/>元素作为props传入,外层div添加的fill样式无法传递到Icon内部的<svg>标签上(fill是SVG专属属性,不是普通CSS样式),导致hover样式不生效。 - 虽然每个Accordion组件独立维护
mouseover状态,但样式作用对象错误,才会出现"要么没效果,要么全触发"的假象。
方案1:让Icon组件接收fill属性(推荐)
修改你的自定义Icon组件,让它支持接收fill属性并传递给内部SVG标签:
// 示例:自定义Icon组件的修改方式 export const ExampleIcon = ({ fill = "currentColor" }) => { return ( <svg fill={fill} width="20" height="20" viewBox="0 0 24 24"> {/* SVG路径内容 */} </svg> ) }
然后修改Accordion组件,直接给Icon传递fill属性,同时调整父组件传参方式(传递组件而非渲染完成的元素):
修改后的Accordion组件
import React, { useState } from 'react'; import { Down } from "../../../../Shared/icons/Down" export const Accordion = ({ data, icon: Icon }) => { const [isOpen, setIsOpen] = useState(false) const [mouseover, setMouseover] = useState(false) // 统一计算当前颜色 const activeColor = isOpen || mouseover ? '#663FD7' : 'inherit' return ( <div className="accordion_container" onMouseOver={() => setMouseover(true)} onMouseLeave={() => setMouseover(false)} onClick={() => setIsOpen(!isOpen)}> <button className="button_container"> <Icon fill={activeColor} /> {/* 直接给Icon传fill属性 */} <div className="title" style={{ color: activeColor }}> {data} </div> </button> <Down style={{ transform: isOpen ? 'rotate(180deg)' : '', transition: 'transform 180ms ease', fill: activeColor }}/> </div> ) }
修改后的CollapseFilter组件
import { Accordion } from './Accordion'; import { filterData } from '../../../../Shared/BazaData/filterData'; export const CollapseFilter = () => { const data = filterData.name.map((item, index) => { const Icon = filterData.icons[index] return ( <div key={index} className='accordion'> {/* 必须添加key,避免React警告 */} <Accordion icon={Icon} data={item.name} /> </div> ) }) return ( <div> {data} </div> ) }
方案2:用CSS伪类实现hover(更简洁)
如果不想修改Icon组件,直接用CSS的:hover伪类控制样式,无需维护mouseover状态:
添加CSS样式
/* hover状态样式 */ .accordion_container:hover .icon-wrapper svg, .accordion_container:hover .title, .accordion_container:hover .down-icon { fill: #663FD7; color: #663FD7; } /* 展开状态样式 */ .accordion_container.open .icon-wrapper svg, .accordion_container.open .title, .accordion_container.open .down-icon { fill: #663FD7; color: #663FD7; } .down-icon { transition: transform 180ms ease; } .accordion_container.open .down-icon { transform: rotate(180deg); }
修改后的Accordion组件
import React, { useState } from 'react'; import { Down } from "../../../../Shared/icons/Down" export const Accordion = ({ data, icon }) => { const [isOpen, setIsOpen] = useState(false) return ( <div className={`accordion_container ${isOpen ? 'open' : ''}`} onClick={() => setIsOpen(!isOpen)}> <button className="button_container"> <div className="icon-wrapper">{icon}</div> <div className="title">{data}</div> </button> <Down className="down-icon"/> </div> ) }
内容的提问来源于stack exchange,提问作者Inal
相关产品推荐
相关产品推荐

