import React, { useEffect, useState } from 'react'; import isNullOrUndefined from '@douyinfe/semi-foundation/utils/isNullOrUndefined'; import { getUuidShort } from '@douyinfe/semi-foundation/utils/uuid'; import semiGlobal from '../_utils/semi-global'; import { get } from 'lodash'; import cls from 'classnames'; export interface IconProps { id?: number; component?: React.ReactNode; size?: number; className?: string; type?: string; customIconCls?: string; } function Icon(props: IconProps = {}) { const { id: propsId, className, customIconCls, ...rest } = props; const globalIndicator = get(semiGlobal, 'config.overrideDefaultProps.Spin.indicator'); if (globalIndicator && React.isValidElement(globalIndicator)) { return React.cloneElement(globalIndicator, { className: cls({ [customIconCls]: customIconCls, [className]: className }) } as any); } /** * NOTE(SSR / Next.js): * We must keep the original SVG implementation based on + url(#id). * However, generating ids via module-level counters or random values during render can * cause hydration mismatch (server/client ids differ). * * Strategy: * - On SSR and the client's initial render, use a stable fallback id to keep markup identical. * - After mount (client-only), replace it with an instance-unique id to avoid cross-instance * collisions where one Spin could affect another via duplicate ids. * * If consumers pass `props.id`, we treat it as a stable explicit seed. */ const fallbackId = 'linearGradient-semi-spin'; const [gradientId, setGradientId] = useState(() => { if (!isNullOrUndefined(propsId)) { return `linearGradient-${propsId}`; } return fallbackId; }); useEffect(() => { if (!isNullOrUndefined(propsId)) { // Explicit id is stable; no need to mutate after mount. setGradientId(`linearGradient-${propsId}`); return; } const unique = getUuidShort({ prefix: 'semi-spin-gradient' }); setGradientId(`linearGradient-${unique}`); }, [propsId]); return ( ); } export default Icon;