Custom Component gets re-rendered everytime when cmd/shift/control is pressed

Hello Retool Community,

Your charts are great but I am trying to use custom components that uses Highcharts for further customization.

But the custom component got re-rendered every time when I press

  • shift
  • control
  • cmd

I added logging to see what's going on apparently it got re-rendered every time I press shift, control or cmd. How to prevent this?

Attached is the source code of the custom component.

import { type FC, useRef, useEffect } from 'react'
import Highcharts from 'highcharts'
import { Retool } from '@tryretool/custom-component-support'

export const BarChart: FC = () => {
  const chartContainerRef = useRef<HTMLDivElement>(null)
  
  const [data, setData] = Retool.useStateArray({ name: 'data' })
  const [categories, setCategories] = Retool.useStateArray({ name: 'categories' })

  console.log("custom component called");
  useEffect(() => {
    if (chartContainerRef.current) {

      const options: Highcharts.Options = {
        chart: {
          type: 'bar'
        },
        xAxis: {
          categories: categories
        },
        series: [{ data }],
      };
      
      Highcharts.chart(chartContainerRef.current, options)
    }
  }, [data, categories]);

  return <div ref={chartContainerRef} />
}

Check the video too for reference.

1 Like

in case you are trying to reproduce.

cc: @Jack_T @lindakwoo

Hey @abdulhamed , Welcome To Retool Community !

I can try this custom component code on my app. You're right—when I press any keyboard key, the chart refreshes or re-renders. I found a solution!

I've updated the custom component code with the following changes:

  • Added chartRef to persist the Highcharts instance.
  • Modified the useEffect logic to ensure the chart is only created once, and updated thereafter.
  • Used JSON.stringify in the dependency array for a deep comparison.

const chartRef = useRef<Highcharts.Chart | null>(null) // Store chart instance

[JSON.stringify(data), JSON.stringify(categories)]); // Deep comparison to prevent unnecessary runs


import { type FC, useRef, useEffect } from 'react'
import Highcharts from 'highcharts'
import { Retool } from '@tryretool/custom-component-support'

export const BarChart: FC = () => {
  const chartContainerRef = useRef<HTMLDivElement>(null)
  const chartRef = useRef<Highcharts.Chart | null>(null) // Store chart instance
  
  const [data, setData] = Retool.useStateArray({ name: 'data' })
  const [categories, setCategories] = Retool.useStateArray({ name: 'categories' })

  console.log("custom component called");

  useEffect(() => {
    if (chartContainerRef.current) {
      const options: Highcharts.Options = {
        chart: {
          type: 'bar'
        },
        xAxis: {
          categories: categories
        },
        series: [{ data }],
      };

      if (!chartRef.current) {
        // Create chart only if it doesn’t exist
        chartRef.current = Highcharts.chart(chartContainerRef.current, options)
      } else {
        // Update existing chart instead of recreating
        chartRef.current.update(options, true)
      }
    }

    // Cleanup on unmount
    return () => {
      if (chartRef.current) {
        chartRef.current.destroy()
        chartRef.current = null
      }
    }
  }, [JSON.stringify(data), JSON.stringify(categories)]); // Deep comparison to prevent unnecessary runs

  return <div ref={chartContainerRef} />
}

6 Likes

life-saving! Thanks man appreciate it

1 Like