Skip to content

Commit

Permalink
Use useSyncExternalStore for React hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
barvian committed Dec 15, 2024
1 parent 4f759f0 commit e626a3f
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 56 deletions.
5 changes: 5 additions & 0 deletions .changeset/famous-drinks-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@number-flow/react': patch
---

Use useSyncExternalStore for hooks
43 changes: 20 additions & 23 deletions packages/react/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
formatToData,
type Data,
NumberFlowLite,
prefersReducedMotion,
prefersReducedMotion as _prefersReducedMotion,
canAnimate as _canAnimate,
define
} from 'number-flow'
Expand Down Expand Up @@ -275,28 +275,25 @@ export function NumberFlowGroup({ children }: { children: React.ReactNode }) {
return <NumberFlowGroupContext.Provider value={value}>{children}</NumberFlowGroupContext.Provider>
}

// SSR-safe canAnimate
export const useIsSupported = () =>
React.useSyncExternalStore(
() => () => {}, // this value doesn't change, but it's useful to specify a different SSR value:
() => _canAnimate,
() => false
)
export const usePrefersReducedMotion = () =>
React.useSyncExternalStore(
(cb) => {
_prefersReducedMotion?.addEventListener('change', cb)
return () => _prefersReducedMotion?.removeEventListener('change', cb)
},
() => _prefersReducedMotion!.matches,
() => false
)

export function useCanAnimate({ respectMotionPreference = true } = {}) {
const [canAnimate, setCanAnimate] = React.useState(false)
const [reducedMotion, setReducedMotion] = React.useState(false)

// Handle SSR:
React.useEffect(() => {
setCanAnimate(_canAnimate)
setReducedMotion(prefersReducedMotion?.matches ?? false)
}, [])

// Listen for reduced motion changes if needed:
React.useEffect(() => {
if (!respectMotionPreference) return
const onChange = ({ matches }: MediaQueryListEvent) => {
setReducedMotion(matches)
}
prefersReducedMotion?.addEventListener('change', onChange)
return () => {
prefersReducedMotion?.removeEventListener('change', onChange)
}
}, [respectMotionPreference])
const isSupported = useIsSupported()
const reducedMotion = usePrefersReducedMotion()

return canAnimate && (!respectMotionPreference || !reducedMotion)
return isSupported && (!respectMotionPreference || !reducedMotion)
}
65 changes: 32 additions & 33 deletions site/src/components/Supported.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,40 @@
import { useCanAnimate } from '@number-flow/react'
import { useIsSupported, usePrefersReducedMotion } from '@number-flow/react'
import clsx from 'clsx/lite'
import Link from './Link'

export default function Supported() {
const canAnimate = useCanAnimate({ respectMotionPreference: false })
const reducedMotion = useCanAnimate({ respectMotionPreference: true })
const isSupported = useIsSupported()
const reducedMotion = usePrefersReducedMotion()
if (isSupported && !reducedMotion) return null

return (
!reducedMotion && (
<>
{/* The only way I could get the blend mode working was to separate the divs which requires fixed sizes */}
<div
role="presentation"
className={clsx(
canAnimate ? 'h-11.5' : 'h-16.5',
'~top-4/8 fixed left-1/2 z-40 -ml-36 w-72 rounded-lg mix-blend-multiply shadow-lg shadow-amber-500/10 dark:shadow-amber-950/20'
)}
/>
<div
role="alert"
className={clsx(
canAnimate ? 'h-11.5' : 'h-16.5',
'~top-4/8 prose prose-current fixed left-1/2 z-50 -ml-36 w-72 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-center text-sm text-amber-800 dark:border-amber-800/30 dark:bg-[#271202] dark:text-amber-50'
)}
>
{!canAnimate ? (
<p>
Your browser doesn't{' '}
<Link href="https://caniuse.com/mdn-css_types_mod">
support NumberFlow’s animations
</Link>
</p>
) : (
<p>Reduce motion is on</p>
)}
</div>
</>
)
<>
{/* The only way I could get the blend mode working was to separate the divs which requires fixed sizes */}
<div
role="presentation"
className={clsx(
reducedMotion ? 'h-11.5' : 'h-16.5',
'~top-4/8 fixed left-1/2 z-40 -ml-36 w-72 rounded-lg mix-blend-multiply shadow-lg shadow-amber-500/10 dark:shadow-amber-950/20'
)}
/>
<div
role="alert"
className={clsx(
reducedMotion ? 'h-11.5' : 'h-16.5',
'~top-4/8 prose prose-current fixed left-1/2 z-50 -ml-36 w-72 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-center text-sm text-amber-800 dark:border-amber-800/30 dark:bg-[#271202] dark:text-amber-50'
)}
>
{reducedMotion ? (
<p>Reduce motion is on</p>
) : (
<p>
Your browser doesn't{' '}
<Link href="https://caniuse.com/mdn-css_types_mod">
support NumberFlow’s animations
</Link>
</p>
)}
</div>
</>
)
}

0 comments on commit e626a3f

Please sign in to comment.