margelo / react-native-graph

📈 Beautiful, high-performance Graphs and Charts for React Native built with Skia
https://margelo.io
MIT License
2.08k stars 118 forks source link

Q: How to render current point data correctly? #8

Closed DDushkin closed 2 years ago

DDushkin commented 2 years ago

Initially, the UI on gestures works pretty well.

without setState

But if I try to render the current point price and timestamp I get a laggy thing. Here is an example of code with setState:

    const onPointSelected = useCallback(
        (p) => {
            console.log(p);

            const selectedPrice = formatAmount(p.value, fiatCurrency.code as CurrencyName, "fiat");
            const timestamp = DateTime.fromSeconds(p.date);

            setAmountTitle(selectedPrice);
            setTimestamp(timestamp);
        },
        [fiatCurrency.code],
    );

As a result with setState I get slow UI:

with setState

So the question is how to render it properly and smoothly?

mrousavy commented 2 years ago

Yeah, that's because of React Native. It isn't really fast at state updates. Maybe this will improve with concurrent mode. The way to solve this is to use Reanimated Shared Values and update the Text using ReanimatedText.

Angelk90 commented 2 years ago

@DDushkin : Have you tested the enablePanGesture on a physical device on android? Not working.

DDushkin commented 2 years ago

Yeah, that's because of React Native. It isn't really fast at state updates. Maybe this will improve with concurrent mode. The way to solve this is to use Reanimated Shared Values and update the Text using ReanimatedText.

Thanks, I installed AnimateableText from "react-native-animateable-text", and here is the new code

    const title = useSharedValue(assetBalanceFormatted);
    const subtitle = useSharedValue(`${t("main.value")}: ${fiatBalanceFormatted}`);

    const animatedTitle = useAnimatedProps(() => ({
        text: title.value,
    }));
    const animatedSubtitle = useAnimatedProps(() => ({
        text: subtitle.value,
    }));

    const onPointSelected = useCallback(
        (p) => {
            console.log(p);

            const selectedPrice = formatAmount(p.value, fiatCurrency.code as CurrencyName, "fiat");
            const timestamp = DateTime.fromSeconds(p.date);

            title.value = selectedPrice;
            subtitle.value = timestamp.toFormat("d MMM yyyy HH:mm");
        },
        [fiatCurrency.code, subtitle, title],
    );
//...
    <AnimateableText animatedProps={animatedTitle} style={styles.title} />
    <AnimateableText animatedProps={animatedSubtitle} style={styles.subtitle} />

UPD: works now

But I can see now that Cursor is not rendered

Запись экрана 2022-05-06 в 15 50 43

DDushkin commented 2 years ago

@DDushkin : Have you tested the enablePanGesture on a physical device on android? Not working.

Haven't tried on the device yet

Angelk90 commented 2 years ago

@DDushkin : How did you manage to put that control line you see in the first picture?

DDushkin commented 2 years ago

@DDushkin : How did you manage to put that control line you see in the first picture?

Added it manually in a fork

Angelk90 commented 2 years ago

@DDushkin : How did you manage to put that control line you see in the first picture?

Added it manually in a fork

It would be great if you could do a pr.

SaiEashwarKS commented 1 year ago

I also needed to display a formatted timestamp without using react-native-animateable-text. I tried using reanimated's shared value, but ran into difficulties formatting the timestamp within the worklet function since I couldn't call methods of Date or other libraries like dayjs. To work around this, I used react-native's TextInput and updated the text using setNativeProps, which seems to be working seamlessly.

So the corresponding props of LineGraph would be like this

onGestureStart={() => {
    gestureStarted.current = true;
}}
onPointSelected={(point) => {
    if (!gestureStarted.current) {
        return;
    }

    selectedPriceRef.current?.setNativeProps({
        text: `${point.value}, ${dayjs(point.date).format('DD MMM')}`,
    });
}}
onGestureEnd={() => {
    gestureStarted.current = false;
    selectedPriceRef.current?.setNativeProps({ text: '' });
}}

The refs are defined as

const selectedPriceRef = useRef<TextInput>(null);
const gestureStarted = useRef(false);

And the TextInput would be like this

<TextInput
  underlineColorAndroid="transparent"
  editable={false}
  ref={selectedPriceRef}
/>