FormidableLabs / victory-native-xl

A charting library for React Native with a focus on performance and customization.
https://commerce.nearform.com/open-source/victory-native
560 stars 33 forks source link

isActive doesn't reliably change to false at the end of a gesture #263

Open stevengoldberg opened 2 months ago

stevengoldberg commented 2 months ago

Describe Your Environment

What version of victory-native-xl are you using? 40.1.1

What version of React and React Native are you using? react 18.2.0, react-native 0.73.6

What version of Reanimated and React Native Skia are you using? reanimated 3.8.1, skia 0.1.221

Are you using Expo or React Native CLI? expo

What platform are you on? (e.g., iOS, Android) iOS

Describe the Problem

When performing a pan gesture on a CartesianChart, isActive sometimes remains true at the end of the gesture. I was able to reproduce this issue in the example app. My app implements very similar logic.

https://github.com/FormidableLabs/victory-native-xl/assets/2230992/d3afa018-bc65-468a-a257-e0e8bff82b78

Expected behavior: At the end of the gesture, isActive should become false and the text below the chart should revert to "Single or multi-touch the chart"

Actual behavior: isActive sometimes remains true at the end of the gesture, so that the text below the chart continues to display the last-touched value.

stevengoldberg commented 2 months ago

Actually after experimenting some more I'm not sure the value of isActive is actually the problem here. It seems like there's some other race condition happening.

edit: I believe the problem is actually with the implementation of AnimatedText in the example app.

Passing an initialValue string instead of value={text.value} seems to prevent the issue.

import * as React from "react";
import { TextInput, type TextInputProps } from "react-native";
import Reanimated, {
  useAnimatedProps,
  type SharedValue,
} from "react-native-reanimated";

const AnimText = Reanimated.createAnimatedComponent(TextInput);
Reanimated.addWhitelistedNativeProps({ text: true });

type AnimatedTextProps = Omit<TextInputProps, "editable" | "value"> & {
  text: SharedValue<string>;
  initialValue: string;
  style?: React.ComponentProps<typeof AnimText>["style"];
};

export function AnimatedText({
  text,
  initialValue,
  ...rest
}: AnimatedTextProps) {
  const animProps = useAnimatedProps(() => {
    return {
      text: text.value,
    };
  });

  return (
    <AnimText
      {...rest}
      value={initialValue}
      // @ts-ignore
      animatedProps={animProps}
      editable={false}
    />
  );
}