/**
* NoSsr Component - Lynx 版 MUI NoSsr
* 一比一对应 MUI NoSsr
*
* NoSsr 用于跳过服务端渲染 (SSR),仅在客户端渲染子元素
*
* 功能:
* - 延迟渲染 (defer mode)
* - 占位符支持 (fallback)
* - 首次挂载检测
*/
// Hooks 类型在 lynx.d.ts 中声明
export interface NoSsrProps {
/** 子元素 */
children?: any
/** 是否延迟渲染 (使用 requestAnimationFrame/setTimeout) */
defer?: boolean
/** 服务端渲染时的占位符 */
fallback?: any
}
/**
* useEnhancedEffect - 在客户端使用 useLayoutEffect,服务端使用 useEffect
* 对应 MUI 的 useEnhancedEffect
*/
const useEnhancedEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect
/**
* NoSsr 组件 - 完整实现
*
* 用法:
* ```tsx
* // 基础用法
*
*
*
*
* // 延迟渲染 - 使用 requestAnimationFrame
*
*
*
*
* // 带占位符
* }>
*
*
* ```
*/
export function NoSsr(props: NoSsrProps) {
const { children, defer = false, fallback = null } = props
// 挂载状态
const [mounted, setMounted] = useState(false)
// 使用 useEnhancedEffect 确保在客户端执行
useEnhancedEffect(() => {
if (!defer) {
// 立即挂载
setMounted(true)
}
}, [defer])
// defer 模式:延迟到下一帧
useEffect(() => {
if (defer) {
// 使用 requestAnimationFrame 或 setTimeout 延迟渲染
// 这允许浏览器先完成当前帧的渲染
let rafId: number
let timeoutId: ReturnType
if (typeof requestAnimationFrame !== 'undefined') {
rafId = requestAnimationFrame(() => {
setMounted(true)
})
} else {
timeoutId = setTimeout(() => {
setMounted(true)
}, 0)
}
return () => {
if (rafId) {
cancelAnimationFrame(rafId)
}
if (timeoutId) {
clearTimeout(timeoutId)
}
}
}
return undefined
}, [defer])
// 未挂载时显示占位符
if (!mounted) {
return fallback
}
// 已挂载,渲染子元素
return children
}
export default NoSsr