margelo / react-native-graph

📈 Beautiful, high-performance Graphs and Charts for React Native built with Skia

Home Page:https://margelo.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Infinite loop with callbacks

DDushkin opened this issue · comments

Hello, thanks for the package!
Everything was good until I started rendering current point price and timestamp near the graph
The code like this

const [amountTitle, setAmountTitle] = useState(assetBalanceFormatted);
const [timestamp, setTimestamp] = useState<DateTime>();
//...
<Center>
   <AssetIconText
      currency={assetName}
      subtitle={amountTitle ? amountTitle : assetBalanceFormatted}
   />
   <Text fontWeight="500" color="text.secondary">
      {timestamp
         ? timestamp.toFormat("d MMM yyyy HH:mm")
         : `${t("main.value")}: ${fiatBalanceFormatted}`}
   </Text>
<Center>
<LineGraph
     style={styles.graph}
     animated={true}
     color="#067dfb"
     dotColor="#ffffff"
     gradientColors={["#067dfb4d", "#37385f1a"]}
     lineThickness={1.5}
     points={graph.data.points}
     enablePanGesture={true}
     onGestureStart={() => hapticFeedback("impactLight")}
     onPointSelected={(p) => {
        console.log(p);
        const selectedPrice = formatAmount(
             p.value,
             fiatCurrency.code as CurrencyName,
             "fiat",
         );
         const timestamp = DateTime.fromSeconds(p.date);

         setAmountTitle(selectedPrice);
         setTimestamp(timestamp);
     }}
     onGestureEnd={() => {
         setAmountTitle("");
         setTimestamp(undefined);
     }}
/>

Creates infinite loop onPointSelected fires at the first render and then onGestureEnd fires next and so on

Oh yeah that's because it isn't memoized.

Try

const onPointSelected = useCallback(() => {
        console.log(p);
        const selectedPrice = formatAmount(
             p.value,
             fiatCurrency.code as CurrencyName,
             "fiat",
         );
         const timestamp = DateTime.fromSeconds(p.date);

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

return <LineGraph YOURCODE />

Thanks!

This problem still exists. onPointSelected works fine but when i set a function on onGestureEnd then it loops over and over again on both onPointSelected and onGestureEnd.

const on_point_selected = useCallback((point) => { setMovingPrice(point.value) console.log(point) }, [])
const reset_price_title = useCallback(() => { setMovingPrice(token.price) console.log('s') }, [])

<LineGraph points={chartHistory} style={[styles.graph, { width: SIZE }]} animated={true} enablePanGesture={true} onGestureStart={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)} onPointSelected={(p) => on_point_selected(p)} onGestureEnd={() => reset_price_title()} indicatorPulsating={true} TopAxisLabel={() => <AxisLabels date={chartHistoryMax.date} value={chartHistoryMax.value.toFixed(2)} />} BottomAxisLabel={() => <AxisLabels date={chartHistoryMin?.date} value={chartHistoryMin?.value.toFixed(2)} left={true} />} verticalPadding={10} color={isLight ? token.theme_color : token.theme_color == '#000000' ? '#ffffff' : token.theme_color} lineThickness={2} />

Can you use the memoized callbacks directly, instead of wrapping them with a arrow function in the LineGraph's props like:

<LineGraph
  ...
  // Memoize this too
  onGestureStart={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)}
  // Use callbacks like this:
  onPointSelected={on_point_selected}
  onGestureEnd={reset_price_title}
  ...
/>

Works great!