recharts: CartesianGrid - Can no longer render CartesianGrid alone
Discussed in https://github.com/recharts/recharts/discussions/3906
<div type='discussions-op-text'>Originally posted by sahillihas2021 October 26, 2023 I am displaying a multilinechart (Having multiple Y-Axis), it’s working perfectly fine in my local, but when I am deploying it to the internal dev server, it is giving an error for the same code on : ERROR
Cannot read properties of null (reading ‘ticks’) TypeError: Cannot read properties of null (reading ‘ticks’) at CartesianGrid.render (https://react.qa.he.az.devmfi.net/static/js/bundle.js:197529:66) at finishClassComponent (https://react.qa.he.az.devmfi.net/static/js/bundle.js:167192:35) at updateClassComponent (https://react.qa.he.az.devmfi.net/static/js/bundle.js:167149:28) at beginWork (https://react.qa.he.az.devmfi.net/static/js/bundle.js:168775:20) at HTMLUnknownElement.callCallback (https://react.qa.he.az.devmfi.net/static/js/bundle.js:153766:18) at Object.invokeGuardedCallbackDev (https://react.qa.he.az.devmfi.net/static/js/bundle.js:153810:20) at invokeGuardedCallback (https://react.qa.he.az.devmfi.net/static/js/bundle.js:153867:35) at beginWork$1 (https://react.qa.he.az.devmfi.net/static/js/bundle.js:173741:11) at performUnitOfWork (https://react.qa.he.az.devmfi.net/static/js/bundle.js:172988:16) at workLoopSync (https://react.qa.he.az.devmfi.net/static/js/bundle.js:172911:9)
Due to: [Refer the Code below)
yAxisComponents.push(
<YAxis
key={yAxisId}
yAxisId={yAxisId}
orientation={orientation}
axisLine={{ display: 'none' }}
tickLine={{ display: 'none' }}
tick={{
fill: tickColor,
fontSize: 16,
fontWeight: 'bold'
}}
/>
);
Code:
import React, { useState, useEffect } from 'react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip } from 'recharts';
import PropTypes from 'prop-types';
import Loading from '../../Loading/Loading';
function MultipleYAxesLineChart({ data, CustomXAxisTick, loading }) {
MultipleYAxesLineChart.propTypes = {
data: PropTypes.array.isRequired,
CustomXAxisTick: PropTypes.array.isRequired,
loading: PropTypes.bool.isRequired,
};
const [groupedData, setGroupedData] = useState({});
useEffect(() => {
const grouped = data.reduce((result, item) => {
const { DeviceId } = item;
if (!result[DeviceId]) {
result[DeviceId] = [];
}
result[DeviceId].push(item);
return result;
}, {});
setGroupedData(grouped);
}, [data]);
const deviceIds = Object.keys(groupedData);
const numYAxes = deviceIds.length;
const customColorScale = ['#72CCC0', '#5E89D8', '#FFD00A', '#A16CB4', '#6A864E', '#7E682D'];
const [slicedDataByDeviceId, setSlicedDataByDeviceId] = useState({});
useEffect(() => {
const updatedSlicedData = {};
const firstTimestamp = CustomXAxisTick[0];
const lastTimestamp = CustomXAxisTick[CustomXAxisTick.length - 1];
for (const DeviceId in groupedData) {
const deviceData = groupedData[DeviceId];
const extractedData = [];
for (let i = firstTimestamp; i <= lastTimestamp; i += 60 * 1000) {
const timestamp = i;
const entry = deviceData.find((item) => item.sensorTimestamp === i);
if (entry) {
extractedData.push(entry);
}
}
updatedSlicedData[DeviceId] = extractedData;
}
setSlicedDataByDeviceId(updatedSlicedData);
}, [groupedData, CustomXAxisTick]);
const yAxisComponents = [];
for (let i = 0; i < numYAxes; i += 1) {
const orientation = i < 3 ? 'left' : 'right';
const yAxisId = `${orientation}${i + 1}`;
const tickColor = customColorScale[i % customColorScale.length];
yAxisComponents.push(
<YAxis
key={yAxisId}
yAxisId={yAxisId}
orientation={orientation}
axisLine={{ display: 'none' }}
tickLine={{ display: 'none' }}
tick={{
fill: tickColor,
fontSize: 16,
fontWeight: 'bold',
}}
/>
);
}
const [hiddenLines, setHiddenLines] = useState({});
const CustomLegend = () => {
const toggleLineVisibility = (DeviceId) => {
setHiddenLines((prevHiddenLines) => ({
...prevHiddenLines,
[DeviceId]: !prevHiddenLines[DeviceId],
}));
};
const legendItems = [];
for (const DeviceId in groupedData) {
const deviceData = groupedData[DeviceId];
if (deviceData.length > 0) {
const { KPI_Label } = deviceData[0];
const color = customColorScale[legendItems.length % customColorScale.length];
legendItems.push(
<div
key={DeviceId}
style={{
display: 'flex',
alignItems: 'center',
marginRight: '20px',
cursor: 'pointer',
textDecoration: hiddenLines[DeviceId] ? 'line-through' : 'none',
}}
onClick={() => {
toggleLineVisibility(DeviceId);
}}
>
<div style={{ backgroundColor: color, width: '20px', height: '6px', marginRight: '5px' }} />
<div>{KPI_Label}</div>
</div>
);
}
}
return <div style={{ display: 'flex', alignItems: 'center' }}>{legendItems}</div>;
};
const containerStyle = {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
width: '1300px',
};
const [allLegendsClicked, setAllLegendsClicked] = useState(false);
useEffect(() => {
const areAllClicked = deviceIds.every((DeviceId) => hiddenLines[DeviceId]);
setAllLegendsClicked(areAllClicked);
}, [hiddenLines, deviceIds]);
const CustomTooltip = ({ active, payload, label }) => {
if (active && payload && payload.length) {
const formatDate = (timestamp) => {
const date = new Date(timestamp);
const formattedDate = date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
});
return formattedDate;
};
return (
<div style={{ background: 'white', border: '1px solid #ccc', padding: '10px', textAlign: 'left' }}>
<div style={{ color: 'black', marginBottom: '5px' }}>Time: {formatDate(label)}</div>
{payload.map((dataItem, index) => (
<div key={`tooltip-item-${index}`} style={{ display: 'flex', alignItems: 'center', marginBottom: '5px' }}>
<div style={{ width: '20px', height: '6px', background: dataItem.color, marginRight: '5px' }}></div>
<div style={{ color: 'black' }}>
{dataItem.payload.KPI_Label}: {dataItem.value || 0} {dataItem.payload.Unit_of_Measure}
</div>
</div>
))}
</div>
);
}
return null;
};
CustomTooltip.propTypes = {
active: PropTypes.bool,
payload: PropTypes.arrayOf(
PropTypes.shape({
color: PropTypes.string,
value: PropTypes.number,
name: PropTypes.string,
})
),
label: PropTypes.string,
};
return (
<div style={containerStyle}>
{loading || data.length === 0 ? (
<Loading />
) : (
<>
<div>
<LineChart width={1300} height={400} data={slicedDataByDeviceId} margin={{ right: 30, bottom: 40 }}>
<CartesianGrid strokeDasharray="3 3" />
{allLegendsClicked ? null : (
<XAxis
dataKey="sensorTimestamp"
type="category"
allowDuplicatedCategory={false}
ticks={CustomXAxisTick}
interval={0}
tickFormatter={(timestamp, index) => {
const date = new Date(timestamp);
const hours = date.getHours();
const minutes = date.getMinutes();
const ampm = hours >= 12 ? 'PM' : 'AM';
const formattedHours = hours % 12 || 12;
const formattedMinutes = minutes.toString().padStart(2, '0');
if (index == 0) {
const curTimestamp = CustomXAxisTick[index + 1];
const timeDiff = curTimestamp - timestamp;
if (timeDiff >= 23 * 3600 * 1000 && timeDiff <= 25 * 3600 * 1000) {
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
];
const month = months[date.getMonth()];
const day = date.getDate();
return `${month}-${day}`;
}
} else if (index > 0) {
const prevTimestamp = CustomXAxisTick[index - 1];
const timeDiff = timestamp - prevTimestamp;
if (timeDiff >= 23 * 3600 * 1000 && timeDiff <= 25 * 3600 * 1000) {
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
];
const month = months[date.getMonth()];
const day = date.getDate();
return `${month}-${day}`;
}
}
return `${formattedHours}:${formattedMinutes} ${ampm}`;
}}
tick={{ fontSize: 14 }}
tickMargin={25}
/>
)}
{yAxisComponents}
<Tooltip content={<CustomTooltip />} />
{deviceIds.slice(0, numYAxes).map((DeviceId, index) => (
<Line
key={DeviceId}
type="monotone"
dataKey="value"
name={DeviceId}
data={slicedDataByDeviceId[DeviceId]}
yAxisId={yAxisComponents[index].props.yAxisId}
stroke={customColorScale[index % customColorScale.length]}
hide={hiddenLines[DeviceId]}
dot={false}
/>
))}
</LineChart>
</div>
<CustomLegend />
</>
)}
</div>
);
}
export default MultipleYAxesLineChart;
The above code is accepting the data (To be displayed on the UI Line chart), CustomXAxisTick (X-axis values), loading (To load the page/refresh).
How can I fix this as I am not able to replicate the same scenario in my local server (Laptop).
Thankyou !</div>
About this issue
- Original URL
- State: closed
- Created 8 months ago
- Comments: 15 (15 by maintainers)
Commits related to this issue
- fix(deps): update dependency recharts to v2.9.1 (#5194) [](https://renovatebot.com) This PR contains the following updates: | Package ... — committed to SAP/ui5-webcomponents-react by renovate[bot] 8 months ago
- chore(deps): update dependency recharts to v2.9.3 (#344) [](https://renovatebot.com) This PR contains the following updates: | Pa... — committed to specfy/specfy by renovate[bot] 8 months ago
Released fix in 2.9.1, resolving