Memoized DefaultChart to make sure it does not change unless its props also change

This commit is contained in:
Alejandro Celaya 2021-09-18 12:29:15 +02:00
parent 039a56f410
commit 58ee123cef
3 changed files with 26 additions and 16 deletions

View file

@ -14,5 +14,8 @@
"process": true, "process": true,
"setImmediate": true "setImmediate": true
}, },
"ignorePatterns": ["src/service*.ts"] "ignorePatterns": ["src/service*.ts"],
"rules": {
"complexity": "off"
}
} }

View file

@ -43,7 +43,7 @@ const toDate = (date?: string | Date): Date | undefined => typeof date === 'stri
export const ShortUrlForm = ( export const ShortUrlForm = (
TagsSelector: FC<TagsSelectorProps>, TagsSelector: FC<TagsSelectorProps>,
DomainSelector: FC<DomainSelectorProps>, DomainSelector: FC<DomainSelectorProps>,
): FC<ShortUrlFormProps> => ({ mode, saving, onSave, initialState, selectedServer }) => { // eslint-disable-line complexity ): FC<ShortUrlFormProps> => ({ mode, saving, onSave, initialState, selectedServer }) => {
const [ shortUrlData, setShortUrlData ] = useState(initialState); const [ shortUrlData, setShortUrlData ] = useState(initialState);
const isEdit = mode === 'edit'; const isEdit = mode === 'edit';
const changeTags = (tags: string[]) => setShortUrlData({ ...shortUrlData, tags: tags.map(normalizeTag) }); const changeTags = (tags: string[]) => setShortUrlData({ ...shortUrlData, tags: tags.map(normalizeTag) });

View file

@ -1,4 +1,4 @@
import { useState } from 'react'; import { useState, memo } from 'react';
import { Doughnut, Bar } from 'react-chartjs-2'; import { Doughnut, Bar } from 'react-chartjs-2';
import { keys, values } from 'ramda'; import { keys, values } from 'ramda';
import classNames from 'classnames'; import classNames from 'classnames';
@ -53,7 +53,7 @@ const generateChartDatasets = (
borderWidth: 2, borderWidth: 2,
}; };
if (!isBarChart) { if (!isBarChart || highlightedData.every((value) => value === 0)) {
return [ mainDataset ]; return [ mainDataset ];
} }
@ -102,7 +102,7 @@ const chartElementAtEvent = (
const statsAreDefined = (stats: Stats | undefined): stats is Stats => !!stats && Object.keys(stats).length > 0; const statsAreDefined = (stats: Stats | undefined): stats is Stats => !!stats && Object.keys(stats).length > 0;
const DefaultChart = ( const DefaultChart = memo((
{ isBarChart = false, stats, max, highlightedStats, highlightedLabel, onClick }: DefaultChartProps, { isBarChart = false, stats, max, highlightedStats, highlightedLabel, onClick }: DefaultChartProps,
) => { ) => {
const Component = isBarChart ? Bar : Doughnut; const Component = isBarChart ? Bar : Doughnut;
@ -150,20 +150,27 @@ const DefaultChart = (
const chartData = generateChartData(isBarChart, labels, data, highlightedData, highlightedLabel); const chartData = generateChartData(isBarChart, labels, data, highlightedData, highlightedLabel);
const height = determineHeight(isBarChart, labels); const height = determineHeight(isBarChart, labels);
// Provide a key based on the height, so that every time the dataset changes, a new chart is rendered const renderChartComponent = (customKey: string) => (
return (
<div className="row">
<div className={classNames('col-sm-12', { 'col-md-7': !isBarChart })}>
<Component <Component
ref={(element) => { ref={(element) => {
setChartRef(element ?? undefined); setChartRef(element ?? undefined);
}} }}
key={height} key={`${height}_${customKey}`}
data={chartData} data={chartData}
options={options} options={options}
height={height} height={height}
getElementAtEvent={chartElementAtEvent(labels, onClick) as any} getElementAtEvent={chartElementAtEvent(labels, onClick) as any}
/> />
);
// Provide a key based on the height, so that every time the dataset changes, a new chart is rendered
return (
<div className="row">
<div className={classNames('col-sm-12', { 'col-md-7': !isBarChart })}>
{/* It's VERY IMPORTANT to render two different components here, as one has 1 dataset and the other has 2 */}
{/* Using the same component causes a crash when switching from 1 to 2 datasets, and then back to 1 dataset */}
{highlightedStats !== undefined && renderChartComponent('with_stats')}
{highlightedStats === undefined && renderChartComponent('without_stats')}
</div> </div>
{!isBarChart && ( {!isBarChart && (
<div className="col-sm-12 col-md-5"> <div className="col-sm-12 col-md-5">
@ -172,6 +179,6 @@ const DefaultChart = (
)} )}
</div> </div>
); );
}; });
export default DefaultChart; export default DefaultChart;