All files / src/components/AnimatePresence use-presence.ts

91.3% Statements 21/23
75% Branches 9/12
71.43% Functions 5/7
88.89% Lines 16/18

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 8934x 34x       34x                                                                 34x 382x   382x   702x           234x 234x   234x   234x                                             34x       34x       34x 66x 234x  
import { useContext, useEffect } from "react"
import {
    PresenceContext,
    PresenceContextProps,
} from "../../context/PresenceContext"
import { useConstant } from "../../utils/use-constant"
 
export type SafeToRemove = () => void
 
type AlwaysPresent = [true, null]
 
type Present = [true]
 
type NotPresent = [false, SafeToRemove]
 
/**
 * When a component is the child of `AnimatePresence`, it can use `usePresence`
 * to access information about whether it's still present in the React tree.
 *
 * ```jsx
 * import { usePresence } from "framer-motion"
 *
 * export const Component = () => {
 *   const [isPresent, safeToRemove] = usePresence()
 *
 *   useEffect(() => {
 *     !isPresent && setTimeout(safeToRemove, 1000)
 *   }, [isPresent])
 *
 *   return <div />
 * }
 * ```
 *
 * If `isPresent` is `false`, it means that a component has been removed the tree, but
 * `AnimatePresence` won't really remove it until `safeToRemove` has been called.
 *
 * @public
 */
export function usePresence(): AlwaysPresent | Present | NotPresent {
    const context = useContext(PresenceContext)
 
    if (context === null) return [true, null]
 
    const { isPresent, onExitComplete, register } = context
 
    // It's safe to call the following hooks conditionally (after an early return) because the context will always
    // either be null or non-null for the lifespan of the component.
 
    // Replace with useOpaqueId when released in React
    const id = useUniqueId()
    useEffect(() => register(id), [])
 
    const safeToRemove = () => onExitComplete?.(id)
 
    return !isPresent && onExitComplete ? [false, safeToRemove] : [true]
}
 
/**
 * Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present.
 * There is no `safeToRemove` function.
 *
 * ```jsx
 * import { useIsPresent } from "framer-motion"
 *
 * export const Component = () => {
 *   const isPresent = useIsPresent()
 *
 *   useEffect(() => {
 *     !isPresent && console.log("I've been removed!")
 *   }, [isPresent])
 *
 *   return <div />
 * }
 * ```
 *
 * @public
 */
export function useIsPresent() {
    return isPresent(useContext(PresenceContext))
}
 
export function isPresent(context: PresenceContextProps | null) {
    return context === null ? true : context.isPresent
}
 
let counter = 0
const incrementId = () => counter++
const useUniqueId = () => useConstant(incrementId)