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

如何用ReactJS TypeScript为Spinner组件创建动态CSS类?

实现React Spinner动态CSS类的方案

要实现根据size、transition等props生成动态CSS类并添加到组件classList的效果,有两种可靠方案,下面分别说明:

方案一:使用CSS-in-JS库(推荐)

Chakra UI这类组件库本质依赖CSS-in-JS方案(Chakra基于emotion实现),这类库会自动处理动态样式生成、类名唯一化和样式注入,无需手动操作DOM。以emotion为例:

步骤1:安装依赖

npm install @emotion/react @emotion/styled

步骤2:实现Spinner组件

import { css } from '@emotion/react';
import type { FC } from 'react';

type SizeType = "xs" | "sm" | "md" | "lg" | "xl" | "xxl";

interface SpinnerProps {
  size?: SizeType;
  transition?: string; // 例如"0.5s"
  thickness?: string; // 例如"3px"
}

// 定义尺寸映射表
const sizeMap: Record<SizeType, string> = {
  xs: '16px',
  sm: '24px',
  md: '32px',
  lg: '48px',
  xl: '64px',
  xxl: '80px',
};

const Spinner: FC<SpinnerProps> = ({
  size = 'md',
  transition = '0.8s',
  thickness = '3px',
}) => {
  // 基于props动态生成样式
  const spinnerStyles = css`
    width: ${sizeMap[size]};
    height: ${sizeMap[size]};
    border: ${thickness} solid #e2e8f0;
    border-top-color: #3182ce;
    border-radius: 50%;
    animation: spin ${transition} linear infinite;

    @keyframes spin {
      to { transform: rotate(360deg); }
    }
  `;

  return <div css={spinnerStyles} className="custom-spinner"></div>;
};

export default Spinner;

使用时,emotion会自动生成类似css-xxxxxx的唯一类名,将样式注入页面的style标签中,组件最终的classList会包含自定义的custom-spinner和动态生成的类,和Chakra UI的效果完全一致。

方案二:手动实现动态样式注入(适合理解原理)

如果不想依赖第三方库,可以手动创建样式并注入DOM,同时生成唯一类名:

import { useEffect, useMemo } from 'react';
import type { FC } from 'react';

type SizeType = "xs" | "sm" | "md" | "lg" | "xl" | "xxl";

interface SpinnerProps {
  size?: SizeType;
  transition?: string;
  thickness?: string;
}

const sizeMap: Record<SizeType, string> = {
  xs: '16px',
  sm: '24px',
  md: '32px',
  lg: '48px',
  xl: '64px',
  xxl: '80px',
};

// 缓存已生成的类,避免重复注入相同样式
const styleCache = new Map<string, string>();

const Spinner: FC<SpinnerProps> = ({
  size = 'md',
  transition = '0.8s',
  thickness = '3px',
}) => {
  // 生成唯一类名标识,避免重复
  const classKey = useMemo(() => {
    return `spinner-${size}-${transition.replace(/\W/g, '')}-${thickness.replace(/\W/g, '')}`;
  }, [size, transition, thickness]);

  useEffect(() => {
    if (styleCache.has(classKey)) return;

    // 创建style元素并写入动态样式
    const styleElement = document.createElement('style');
    styleElement.textContent = `
      .${classKey} {
        width: ${sizeMap[size]};
        height: ${sizeMap[size]};
        border: ${thickness} solid #e2e8f0;
        border-top-color: #3182ce;
        border-radius: 50%;
        animation: spin-${classKey} ${transition} linear infinite;
      }

      @keyframes spin-${classKey} {
        to { transform: rotate(360deg); }
      }
    `;

    // 将样式注入到页面head中
    document.head.appendChild(styleElement);
    styleCache.set(classKey, classKey);

    // 组件卸载时清理样式(可选)
    return () => {
      document.head.removeChild(styleElement);
      styleCache.delete(classKey);
    };
  }, [classKey, size, transition, thickness]);

  return <div className={`custom-spinner ${classKey}`}></div>;
};

export default Spinner;

这个方案中,我们通过useMemo生成唯一类名,用useEffect动态创建并注入样式,同时用styleCache避免重复生成相同样式。组件的classList会包含自定义的custom-spinner和动态生成的类名,完全符合你的需求。


内容的提问来源于stack exchange,提问作者Steven Ducker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 01:35:27