From e900a9b57f588e32ddd41589457f931c7cd262b0 Mon Sep 17 00:00:00 2001 From: Kyle Tsang <6854874+kyletsang@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:04:17 -0700 Subject: [PATCH] fix: fix warnings in strict mode (#97) --- src/ImperativeTransition.tsx | 7 +-- src/RTGTransition.tsx | 19 +++++++++ src/useRTGTransitionProps.ts | 82 ++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 src/RTGTransition.tsx create mode 100644 src/useRTGTransitionProps.ts diff --git a/src/ImperativeTransition.tsx b/src/ImperativeTransition.tsx index dee5b4f..4c75b00 100644 --- a/src/ImperativeTransition.tsx +++ b/src/ImperativeTransition.tsx @@ -4,6 +4,7 @@ import useIsomorphicEffect from '@restart/hooks/useIsomorphicEffect'; import React, { useRef, cloneElement, useState } from 'react'; import { TransitionComponent, TransitionProps } from './types'; import NoopTransition from './NoopTransition'; +import RTGTransition from './RTGTransition'; export interface TransitionFunctionOptions { in: boolean; @@ -118,12 +119,12 @@ export default function ImperativeTransition({ } export function renderTransition( - Component: TransitionComponent | undefined, + component: TransitionComponent | undefined, runTransition: TransitionHandler | undefined, props: TransitionProps & Omit, ) { - if (Component) { - return ; + if (component) { + return ; } if (runTransition) { return ; diff --git a/src/RTGTransition.tsx b/src/RTGTransition.tsx new file mode 100644 index 0000000..4be781b --- /dev/null +++ b/src/RTGTransition.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import useRTGTransitionProps, { + TransitionProps, +} from './useRTGTransitionProps'; + +export type RTGTransitionProps = TransitionProps & { + component: React.ElementType; +}; + +// Normalizes Transition callbacks when nodeRef is used. +const RTGTransition = React.forwardRef( + ({ component: Component, ...props }, ref) => { + const transitionProps = useRTGTransitionProps(props); + + return ; + }, +); + +export default RTGTransition; diff --git a/src/useRTGTransitionProps.ts b/src/useRTGTransitionProps.ts new file mode 100644 index 0000000..bb80517 --- /dev/null +++ b/src/useRTGTransitionProps.ts @@ -0,0 +1,82 @@ +import { cloneElement, useCallback, useRef } from 'react'; +import useMergedRefs from '@restart/hooks/useMergedRefs'; +import { + TransitionProps as RTGTransitionProps, + TransitionStatus, +} from 'react-transition-group/Transition'; + +export type TransitionProps = RTGTransitionProps & { + children: + | React.ReactElement + | (( + status: TransitionStatus, + props: Record, + ) => React.ReactNode); +}; + +/** + * Normalizes RTG transition callbacks with nodeRef to better support + * strict mode. + * + * @param props Transition props. + * @returns Normalized transition props. + */ +export default function useRTGTransitionProps({ + onEnter, + onEntering, + onEntered, + onExit, + onExiting, + onExited, + addEndListener, + children, + ...props +}: TransitionProps) { + const nodeRef = useRef(null); + const mergedRef = useMergedRefs( + nodeRef, + typeof children === 'function' ? null : (children as any).ref, + ); + + const normalize = + (callback?: (node: HTMLElement, param: any) => void) => (param: any) => { + if (callback && nodeRef.current) { + callback(nodeRef.current, param); + } + }; + + /* eslint-disable react-hooks/exhaustive-deps */ + const handleEnter = useCallback(normalize(onEnter), [onEnter]); + const handleEntering = useCallback(normalize(onEntering), [onEntering]); + const handleEntered = useCallback(normalize(onEntered), [onEntered]); + const handleExit = useCallback(normalize(onExit), [onExit]); + const handleExiting = useCallback(normalize(onExiting), [onExiting]); + const handleExited = useCallback(normalize(onExited), [onExited]); + const handleAddEndListener = useCallback(normalize(addEndListener), [ + addEndListener, + ]); + /* eslint-enable react-hooks/exhaustive-deps */ + + return { + ...props, + nodeRef, + onEnter: handleEnter, + onEntering: handleEntering, + onEntered: handleEntered, + onExit: handleExit, + onExiting: handleExiting, + onExited: handleExited, + addEndListener: handleAddEndListener, + children: + typeof children === 'function' + ? (((status: TransitionStatus, innerProps: Record) => + // TODO: Types for RTG missing innerProps, so need to cast. + children(status, { + ...innerProps, + ref: mergedRef, + })) as any) + : cloneElement(children as React.ReactElement, { + ref: mergedRef, + }), + }; +}