React Scenarios
Search box (debounce + timeout + retry)
tsx
import { useState } from 'react'
import { debounce, Http } from 'nex-lib'
export default function Search() {
const [q, setQ] = useState('')
const run = debounce(async (kw: string) => {
const data = await Http.get('/api/search', { query: { q: kw }, timeoutMs: 3000, retry: { times: 3, delay: 200 } })
// setList(data)
}, 300)
return <input value={q} onChange={e => { setQ(e.target.value); run(e.target.value) }} />
}Countdown (TTL + server time)
tsx
import { useEffect, useState } from 'react'
import { nextAt, nowSeconds, formatDuration, Http, StorageUtils } from 'nex-lib'
export default function Countdown() {
const [remain, setRemain] = useState('00:00:00')
const cached = StorageUtils.getWithTTL('holiday_target')
const computedTarget = nextAt(3, 18)
const target = typeof cached === 'number' ? cached : computedTarget
StorageUtils.setWithTTL('holiday_target', target, 24*60*60*1000)
useEffect(() => {
let t: any
async function tick() {
const now = await Http.get('/api/time', { timeoutMs: 3000, retry: 2 })
.then((r: any) => r.now).catch(() => nowSeconds())
setRemain(formatDuration(Math.max(0, (target - now) * 1000)))
}
t = setInterval(tick, 1000)
return () => clearInterval(t)
}, [])
return <span>{remain}</span>
}UTM merge + same‑origin + copy
tsx
import { createWURL, StorageUtils, browserUtils } from 'nex-lib'
export function Share({ sku }: { sku: string }) {
const w = createWURL(location.href)
const merged = w.replaceParams({ ...w.parseQueryParams(), utm_source: 'web', sku })
async function copy() {
if (!w.isSameOrigin(merged)) throw new Error('unsafe')
StorageUtils.setWithTTL('utm', merged, 7*24*60*60*1000)
await browserUtils.copyToClipboard(merged)
}
return <button onClick={copy}>Copy</button>
}