feat: add hydration support and fallback for Countdown component (#37943)

This commit is contained in:
Wu Tianwei 2026-06-25 18:40:48 +08:00 committed by GitHub
parent aa37c1d833
commit 1d2cc1e475
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 48 additions and 2 deletions

View File

@ -1,4 +1,6 @@
import { act, fireEvent, render, screen } from '@testing-library/react'
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { hydrateRoot } from 'react-dom/client'
import { renderToString } from 'react-dom/server'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import Countdown from '../countdown'
import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '../storage'
@ -46,6 +48,26 @@ describe('Countdown', () => {
// State Management Tests
describe('State Management', () => {
it('should render stored countdown time after hydrating server markup', async () => {
vi.useRealTimers()
const savedTime = 30000
localStorage.setItem(COUNT_DOWN_KEY, String(savedTime))
const container = document.createElement('div')
container.innerHTML = renderToString(<Countdown />)
const root = hydrateRoot(container, <Countdown />)
try {
await waitFor(() => {
expect(container).toHaveTextContent('30')
})
}
finally {
act(() => {
root.unmount()
})
}
})
it('should initialize leftTime from localStorage if available', () => {
const savedTime = 45000
localStorage.setItem(COUNT_DOWN_KEY, String(savedTime))

View File

@ -1,6 +1,7 @@
'use client'
import { useCountDown } from 'ahooks'
import { useEffect, useState } from 'react'
import { useIsClient } from 'foxact/use-is-client'
import { Suspense, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { COUNT_DOWN_TIME_MS, useCountdownLeftTimeValue, useSetCountdownLeftTime } from './storage'
@ -9,6 +10,29 @@ type CountdownProps = {
}
export default function Countdown({ onResend }: CountdownProps) {
const isClient = useIsClient()
if (!isClient)
return <CountdownFallback />
return (
<Suspense fallback={<CountdownFallback />}>
<CountdownContent onResend={onResend} />
</Suspense>
)
}
function CountdownFallback() {
const { t } = useTranslation()
return (
<p className="system-xs-regular text-text-tertiary">
<span>{t('checkCode.didNotReceiveCode', { ns: 'login' })}</span>
</p>
)
}
function CountdownContent({ onResend }: CountdownProps) {
const { t } = useTranslation()
const storedLeftTime = useCountdownLeftTimeValue()
const setStoredLeftTime = useSetCountdownLeftTime()