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

TypeScript中为通用Styled Components传递悬停/点击状态样式Props

用Styled Components实现带状态样式的通用Card组件

核心问题解决思路

  1. Hover状态样式:直接在Styled Components中使用&:hover伪类定义,无需额外传递特殊Props,也支持通过自定义Props注入个性化hover样式。
  2. 点击激活持久状态:通过组件状态(或父组件状态)维护激活标识,给Styled组件传递isActive Props,根据该标识切换样式,实现点击后保持状态直到再次切换。

完整代码实现

第一步:创建StyledCard基础组件

import styled from 'styled-components';

interface StyledCardProps {
  // 自定义基础样式
  baseStyle?: React.CSSProperties;
  // 自定义hover样式
  hoverStyle?: React.CSSProperties;
  // 自定义激活样式
  activeStyle?: React.CSSProperties;
  // 是否处于激活状态
  isActive?: boolean;
}

const StyledCard = styled.div<StyledCardProps>`
  // 基础默认样式
  padding: 16px;
  border-radius: 8px;
  border: 1px solid #e0e0e0;
  cursor: pointer;
  transition: all 0.3s ease;
  box-sizing: border-box;
  width: 100%;
  max-width: 320px;

  // 合并传入的基础样式
  ${props => props.baseStyle && Object.entries(props.baseStyle).map(([key, value]) => `${key}: ${value};`).join('')}

  // Hover状态样式
  &:hover {
    background-color: #f5f5f5;
    // 合并自定义hover样式
    ${props => props.hoverStyle && Object.entries(props.hoverStyle).map(([key, value]) => `${key}: ${value};`).join('')}
  }

  // 激活状态样式
  ${props => props.isActive && `
    background-color: #e3f2fd;
    border-color: #2196f3;
    // 合并自定义激活样式
    ${props.activeStyle && Object.entries(props.activeStyle).map(([key, value]) => `${key}: ${value};`).join('')}
  `}
`;

第二步:封装通用Card组件

import { useState } from 'react';

interface CardProps {
  title: string;
  avatarUrl: string;
  name: string;
  badge?: string;
  baseStyle?: React.CSSProperties;
  hoverStyle?: React.CSSProperties;
  activeStyle?: React.CSSProperties;
  id: string;
}

const Card = ({ title, avatarUrl, name, badge, baseStyle, hoverStyle, activeStyle, id }: CardProps) => {
  // 单Card场景:维护自身激活状态;多Card切换请将activeId提升至父组件
  const [activeId, setActiveId] = useState<string | null>(null);

  const handleCardClick = () => {
    // 点击切换激活状态:已激活则取消,未激活则设置为当前id
    setActiveId(prev => prev === id ? null : id);
  };

  return (
    <StyledCard
      baseStyle={baseStyle}
      hoverStyle={hoverStyle}
      activeStyle={activeStyle}
      isActive={activeId === id}
      onClick={handleCardClick}
    >
      <div style={{ display: 'flex', gap: '12px', alignItems: 'center', marginBottom: '12px' }}>
        <img 
          src={avatarUrl} 
          alt={`${name}的头像`} 
          style={{ width: 48, height: 48, borderRadius: '50%', objectFit: 'cover' }} 
        />
        <div>
          <h3 style={{ margin: 0, fontSize: '18px' }}>{title}</h3>
          <p style={{ margin: '4px 0 0', color: '#666' }}>{name}</p>
        </div>
      </div>
      {badge && (
        <span style={{ 
          padding: '2px 8px', 
          backgroundColor: '#ffeb3b', 
          borderRadius: '4px', 
          fontSize: '12px',
          fontWeight: 500
        }}>
          {badge}
        </span>
      )}
    </StyledCard>
  );
};

第三步:使用示例

// 单Card使用
<Card
  id="card-1"
  title="前端工程师"
  name="张三"
  avatarUrl="https://via.placeholder.com/48"
  badge="在职"
  baseStyle={{ backgroundColor: '#fff' }}
  hoverStyle={{ backgroundColor: '#f0f8ff' }}
  activeStyle={{ backgroundColor: '#e6f7ff', borderColor: '#1890ff' }}
/>

// 多Card切换(需将activeId提升至父组件)
const CardGroup = () => {
  const [activeId, setActiveId] = useState<string | null>(null);
  const cardList = [
    { id: 'card-1', title: '前端工程师', name: '张三', avatarUrl: 'https://via.placeholder.com/48', badge: '在职' },
    { id: 'card-2', title: '后端工程师', name: '李四', avatarUrl: 'https://via.placeholder.com/48', badge: '离职' },
  ];

  return (
    <div style={{ display: 'flex', gap: '16px', padding: '20px' }}>
      {cardList.map(card => (
        <Card
          key={card.id}
          {...card}
          baseStyle={{ backgroundColor: '#fff' }}
          isActive={activeId === card.id}
          onClick={() => setActiveId(prev => prev === card.id ? null : card.id)}
        />
      ))}
    </div>
  );
};

常见问题排查

  • Hover样式不生效:检查是否漏写&(必须用&:hover而非:hover,否则伪类会作用于子元素);检查基础样式的background-color是否覆盖了hover样式;确保添加了transition让变化可见。
  • 激活状态不保持:确认isActive Prop正确关联了状态值;多Card场景下需将激活状态提升至父组件,避免每个Card维护独立状态导致冲突。

内容的提问来源于stack exchange,提问作者Jack Repear

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 23:40:35