Skip to content

A number of StrictMode fixes and updates #99

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 25, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix(useAnimationFrame)!: Make StrictMode safe
BREAKING CHANGE: no longer supports `cancelPrevious` this is always true
  • Loading branch information
jquense committed Nov 22, 2024
commit f0b8066f44f46304cd4a153b025db59c8a36388c
61 changes: 28 additions & 33 deletions src/useAnimationFrame.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { useRef } from 'react'
import { useEffect, useState } from 'react'
import useMounted from './useMounted'
import useStableMemo from './useStableMemo'
import useWillUnmount from './useWillUnmount'

export interface UseAnimationFrameReturn {
cancel(): void
Expand All @@ -11,15 +9,12 @@ export interface UseAnimationFrameReturn {
* Previously registered callbacks will be cancelled
*/
request(callback: FrameRequestCallback): void

/**
* Request for the provided callback to be called on the next animation frame.
* Previously registered callbacks can be cancelled by providing `cancelPrevious`
*/
request(cancelPrevious: boolean, callback: FrameRequestCallback): void
}
type AnimationFrameState = {
fn: FrameRequestCallback
}
/**
* Returns a controller object for requesting and cancelling an animation freame that is properly cleaned up
* Returns a controller object for requesting and cancelling an animation frame that is properly cleaned up
* once the component unmounts. New requests cancel and replace existing ones.
*
* ```ts
Expand All @@ -45,32 +40,32 @@ export interface UseAnimationFrameReturn {
*/
export default function useAnimationFrame(): UseAnimationFrameReturn {
const isMounted = useMounted()
const handle = useRef<number | undefined>()

const cancel = () => {
if (handle.current != null) {
cancelAnimationFrame(handle.current)
}
}
const [animationFrame, setAnimationFrameState] =
useState<AnimationFrameState | null>(null)

useWillUnmount(cancel)
useEffect(() => {
if (!animationFrame) {
return
}

return useStableMemo(
() => ({
request(
cancelPrevious: boolean | FrameRequestCallback,
fn?: FrameRequestCallback,
) {
if (!isMounted()) return
const { fn } = animationFrame
const handle = requestAnimationFrame(fn)
return () => {
cancelAnimationFrame(handle)
}
}, [animationFrame])

if (cancelPrevious) cancel()
const [returnValue] = useState(() => ({
request(callback: FrameRequestCallback) {
if (!isMounted()) return
setAnimationFrameState({ fn: callback })
},
cancel: () => {
if (!isMounted()) return
setAnimationFrameState(null)
},
}))

handle.current = requestAnimationFrame(
fn || (cancelPrevious as FrameRequestCallback),
)
},
cancel,
}),
[],
)
return returnValue
}