diff --git a/src/components/Tooltip/BaseGenericTooltip/index.native.tsx b/src/components/Tooltip/BaseGenericTooltip/index.native.tsx index b48cb55ccea1..f695ebaf0ca3 100644 --- a/src/components/Tooltip/BaseGenericTooltip/index.native.tsx +++ b/src/components/Tooltip/BaseGenericTooltip/index.native.tsx @@ -1,9 +1,9 @@ import {Portal} from '@gorhom/portal'; import React, {useMemo, useRef, useState} from 'react'; -import {InteractionManager, View} from 'react-native'; +import {View} from 'react-native'; // eslint-disable-next-line no-restricted-imports import type {View as RNView} from 'react-native'; -import Animated, {useAnimatedStyle} from 'react-native-reanimated'; +import Animated, {useAnimatedStyle, useSharedValue} from 'react-native-reanimated'; import TransparentOverlay from '@components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/TransparentOverlay/TransparentOverlay'; import Text from '@components/Text'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -40,10 +40,12 @@ function BaseGenericTooltip({ // The width of tooltip's inner content. Has to be undefined in the beginning // as a width of 0 will cause the content to be rendered of a width of 0, // which prevents us from measuring it correctly. - const [contentMeasuredWidth, setContentMeasuredWidth] = useState(); + const [contentMeasuredWidthState, setContentMeasuredWidth] = useState(); + const contentMeasuredWidthAnimated = useSharedValue(0); // The height of tooltip's wrapper. - const [wrapperMeasuredHeight, setWrapperMeasuredHeight] = useState(); + const [wrapperMeasuredHeightState, setWrapperMeasuredHeight] = useState(); + const wrapperMeasuredHeightAnimated = useSharedValue(0); const rootWrapper = useRef(null); const StyleUtils = useStyleUtils(); @@ -58,8 +60,8 @@ function BaseGenericTooltip({ tooltipTargetWidth: targetWidth, tooltipTargetHeight: targetHeight, maxWidth, - tooltipContentWidth: contentMeasuredWidth, - tooltipWrapperHeight: wrapperMeasuredHeight, + tooltipContentWidth: contentMeasuredWidthState, + tooltipWrapperHeight: wrapperMeasuredHeightState, manualShiftHorizontal: shiftHorizontal, manualShiftVertical: shiftVertical, shouldForceRenderingBelow, @@ -75,8 +77,8 @@ function BaseGenericTooltip({ targetWidth, targetHeight, maxWidth, - contentMeasuredWidth, - wrapperMeasuredHeight, + contentMeasuredWidthState, + wrapperMeasuredHeightState, shiftHorizontal, shiftVertical, shouldForceRenderingBelow, @@ -86,7 +88,11 @@ function BaseGenericTooltip({ ); const animationStyle = useAnimatedStyle(() => { - return StyleUtils.getTooltipAnimatedStyles({tooltipContentWidth: contentMeasuredWidth, tooltipWrapperHeight: wrapperMeasuredHeight, currentSize: animation}); + return StyleUtils.getTooltipAnimatedStyles({ + tooltipContentWidth: contentMeasuredWidthAnimated.get(), + tooltipWrapperHeight: wrapperMeasuredHeightAnimated.get(), + currentSize: animation, + }); }); let content; @@ -110,20 +116,17 @@ function BaseGenericTooltip({ ref={rootWrapper} style={[rootWrapperStyle, animationStyle]} onLayout={(e) => { - const {height} = e.nativeEvent.layout; - if (height === wrapperMeasuredHeight) { + const {height, width} = e.nativeEvent.layout; + if (height === wrapperMeasuredHeightAnimated.get()) { return; } + // To avoid unnecessary re-renders of the content container when passing state values to useAnimatedStyle, + // we use SharedValue for managing content and wrapper measurements. + contentMeasuredWidthAnimated.set(width); + wrapperMeasuredHeightAnimated.set(height); + + setContentMeasuredWidth(width); setWrapperMeasuredHeight(height); - // When tooltip is used inside an animated view (e.g. popover), we need to wait for the animation to finish before measuring content. - const target = e.target; - setTimeout(() => { - InteractionManager.runAfterInteractions(() => { - target.measure((x, y, width) => { - setContentMeasuredWidth(width); - }); - }); - }, CONST.ANIMATED_TRANSITION); }} > {content}