如何用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
相关产品推荐
相关产品推荐

