Repository: apache/superset
File: superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx (TypeScript)

Code before:
  } = props;

  const comparisonColumns = useMemo(
    () => [
      { key: 'all', label: t('Display all') },
      { key: '#', label: '#' },
      { key: '△', label: '△' },
      { key: '%', label: '%' },
    ],
    [],
  );

  const timestampFormatter = useCallback(
    (value: DataRecordValue) => {
      if (value == null) {
        return '';
      }
      // In Raw Records mode, don't apply time grain-based formatting
      if (isRawRecords || !timeGrain) {
        return String(value);
      }
      return getTimeFormatterForGranularity(timeGrain)(
        value as number | Date | null | undefined,
      );
    },
    [timeGrain, isRawRecords],
  );
  const [tableSize, setTableSize] = useState<TableSize>({
    width: 0,
    height: 0,
  });
  // keep track of whether column order changed, so that column widths can too
  const [columnOrderToggle, setColumnOrderToggle] = useState(false);
  const [showComparisonDropdown, setShowComparisonDropdown] = useState(false);
  const [selectedComparisonColumns, setSelectedComparisonColumns] = useState([
    comparisonColumns[0].key,
  ]);
  const [hideComparisonKeys, setHideComparisonKeys] = useState<string[]>([]);
  // recalculated totals to display when the search filter is applied (client-side pagination)
  const [displayedTotals, setDisplayedTotals] = useState<D | undefined>(totals);
  const theme = useTheme();

  useEffect(() => {
    setDisplayedTotals(totals);
  }, [totals]);

  // only take relevant page size options
  const pageSizeOptions = useMemo(() => {
    const getServerPagination = (n: number) => n <= rowCount;
    return (
      serverPagination ? SERVER_PAGE_SIZE_OPTIONS : PAGE_SIZE_OPTIONS

Review comment (suggestion):
```suggestion
      value => getTimeFormatterForGranularity(isRawRecords ? undefined : timeGrain)(value),
      [timeGrain, isRawRecords],
```

Code after:

  const comparisonColumns = useMemo(
    () => [
      { key: 'all', label: t('Display all') },
      { key: '#', label: '#' },
      { key: '△', label: '△' },
      { key: '%', label: '%' },
    ],
    [],
  );

  const timestampFormatter = useCallback(
    (value: DataRecordValue) =>
      isRawRecords
        ? String(value ?? '')
        : getTimeFormatterForGranularity(timeGrain)(
            value as number | Date | null | undefined,
          ),
    [timeGrain, isRawRecords],
  );
  const [tableSize, setTableSize] = useState<TableSize>({
    width: 0,
    height: 0,
  });
  // keep track of whether column order changed, so that column widths can too
  const [columnOrderToggle, setColumnOrderToggle] = useState(false);
  const [showComparisonDropdown, setShowComparisonDropdown] = useState(false);
  const [selectedComparisonColumns, setSelectedComparisonColumns] = useState([
    comparisonColumns[0].key,
  ]);
  const [hideComparisonKeys, setHideComparisonKeys] = useState<string[]>([]);
  // recalculated totals to display when the search filter is applied (client-side pagination)
  const [displayedTotals, setDisplayedTotals] = useState<D | undefined>(totals);
  const theme = useTheme();

  useEffect(() => {
    setDisplayedTotals(totals);
  }, [totals]);

  // only take relevant page size options
  const pageSizeOptions = useMemo(() => {
    const getServerPagination = (n: number) => n <= rowCount;
    return (
      serverPagination ? SERVER_PAGE_SIZE_OPTIONS : PAGE_SIZE_OPTIONS
    ).filter(([n]) =>
      serverPagination ? getServerPagination(n) : n <= 2 * data.length,
    ) as SizeOption[];
  }, [data.length, rowCount, serverPagination]);

  const getValueRange = useCallback(
    function getValueRange(key: string, alignPositiveNegative: boolean) {