I try to load data in a graph using a rest api, but it doesn't load for some reason,
can you help me
this is the data that the api brings
{
"avg_soc_fleet": 74.85
}
here is the code
const Rem = () => {
const [avgSoc, SetAvgSoc] = useState({});
useEffect(() => {
axios.get('http://127.0.0.1:8000/other/avgsoc_fleet').then(response => {
SetAvgSoc(response.data);
})
}, []);
const [avgChart] = useState({
labels: ['Soc', 'Pressure', 'Isolation', 'BattVolt24'],
datasets: [
{
data: [Object.values(avgSoc)],
fill: false,
borderColor: '#4bc0c0',
tension: .4
},
]
});
const options = {
legend: {
display: true,
}
};
return (
<div className="grid">
<div className="col-8">
<div className="card">
<Chart type="line" data={avgChart} options={options} />
</div>
</div>
</div>
);
};
export default Rem;
render chart
I hope I can load the data from the api in the graph in react
You need to update the avgChart after getting the response from axios. In that case avgSoc seems unnecessary, try the following:
const Rem = () => {
const [avgChart, setAvgChart] = useState();
useEffect(() => {
axios.get('http://127.0.0.1:8000/other/avgsoc_fleet').then(response => {
const chartOptions={
labels: ['Soc', 'Pressure', 'Isolation', 'BattVolt24'],
datasets: [
{
data: [Object.values(response.data)],
fill: false,
borderColor: '#4bc0c0',
tension: .4
},
]
}
setAvgChart(chartOptions);
})
}, []);
const options = {
legend: {
display: true,
}
};
return (
<div className="grid">
<div className="col-8">
<div className="card">
<Chart type="line" data={avgChart} options={options} />
</div>
</div>
</div>
);
};
export default Rem;
OBS: This is an example of how to proceed, not necessarily the only possible implementation.
Once you get the data, you need to update the avgChart again. use a setState method
const [avgChart, setAvgChart] = useState({
add new useEffect
useEffect(() => {
if(avgSoc.length){
setAvgChart({
...avgChart,
datasets: {
...avgChart.datasets
data: [Object.values(avgSoc)],
}
})
}
}, [avgSoc]);
Related
import { render } from "react-dom";
import HighchartsReact from "highcharts-react-official";
import Highcharts from "highcharts";
const LineChart = () => {
const [chartOptions, setChartOptions] = useState({
series: [{ data: [1, 2, 3] }]
});
const [intervalRef, setIntervalRef] = useState(null);
function toggleLiveData() {
if (!intervalRef) {
setIntervalRef(setInterval(() => {
console.log("add");
setChartOptions((state) => ({
series: [
{
data: [...state.series[0].data, Math.random()]
}
]
}));
}, 500));
} else {
clearInterval(intervalRef);
setIntervalRef(null);
}
}
function handleClick() {
toggleLiveData();
}
return (
<div>
<HighchartsReact highcharts={Highcharts} options={chartOptions} />
<button onClick={handleClick}>toggle live data</button>
</div>
);
};
render(<LineChart />, document.getElementById("root"));
This code dynamically adds points to the chart. It adds the new point every 1/2second and after a point it just clutters the graph how do I remove the extra points and only keep 10 points on the screen at once?
In this case the best is get a chart reference and call the addPoint method directly on a series (with enabled the shift argument).
const LineChart = () => {
...
function toggleLiveData() {
if (!intervalRef) {
setIntervalRef(
setInterval(() => {
if (chartComponent.current) {
chartComponent.current.chart.series[0].addPoint(
Math.random(),
true,
true,
false
);
}
}, 500)
);
} else {
clearInterval(intervalRef);
setIntervalRef(null);
}
}
return (
<div>
<HighchartsReact
ref={chartComponent}
highcharts={Highcharts}
options={chartOptions}
/>
</div>
);
};
Live demo: https://codesandbox.io/s/highcharts-react-demo-forked-58ptz?file=/demo.jsx
API Reference: https://api.highcharts.com/class-reference/Highcharts.Series#addPoint
I am following the documentation here (https://reactjs.org/docs/faq-ajax.html) for AJAX and API's to be able to get my API data, display a loading spinner whilst its loading and then display the chart once its loaded.
I however got this error: Line 31:7: Expected an assignment or function call and instead saw an expression. Code is below:
Chart3.js
import React, { useState, useEffect } from "react";
import { Line } from "react-chartjs-2";
import * as ReactBootStrap from 'react-bootstrap';
import axios from "axios";
function MyComponent() {
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [items, setItems] = useState([]);
const [chartData, setChartData] = useState({});
// Note: the empty deps array [] means
// this useEffect will run once
// similar to componentDidMount()
useEffect(() => {
let Fore = [];
let Act = [];
fetch('https://api.carbonintensity.org.uk/intensity/2020-09-01T15:30Z/2020-09-10T17:00Z')
.then(res => {
console.log(res);
for (const dataObj of res.data.data) {
Fore.push(parseInt(dataObj.intensity.forecast));
Act.push(parseInt(dataObj.intensity.actual));
setIsLoaded(true);
}
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
setIsLoaded(true);
setChartData({
labels: Fore,
datasets: [
{
label: "Carbon Intensity Levels",
data: Act,
backgroundColor: "#F58A07",
borderWidth: 4
}
]
});
setError(error);
}})
}, [])
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div> <ReactBootStrap.Spinner animation="border"/> </div>;
} else {
return (
<div className="App">
<h1></h1>
<div className="chart">
<Line
data={chartData}
options={{
responsive: true,
title: { text: "2020-09-01T15:30Z - 2020-09-10T17:00Z", display: true },
scales: {
yAxes: [
{
ticks: {
autoSkip: true,
maxTicksLimit: 100,
beginAtZero: true
},
gridLines: {
display: false
},
scaleLabel: {
display: true,
labelString: "Actual"
}
}
],
xAxes: [
{
gridLines: {
display: false
},
scaleLabel: {
display: true,
labelString: "Forecast"
}
}
]
}
}} />
</div>
</div>
);
}
}
export default MyComponent;
Its saying the error is at line 31 which is the part of the code "(error) => {..."
Thanks for the help, that solution did not work but I found one that has worked code snippet below.
import React, { useState, useEffect } from "react";
import { Line } from "react-chartjs-2";
import * as ReactBootStrap from 'react-bootstrap';
import axios from "axios";
function MyComponent() {
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [chartData, setChartData] = useState({});
// Note: the empty deps array [] means
// this useEffect will run once
// similar to componentDidMount()
useEffect(() => {
let Fore = [];
let Act = [];
fetch('https://api.carbonintensity.org.uk/intensity/2020-09-01T15:30Z/2020-09-10T17:00Z')
.then(res => {
console.log(res);
for (const dataObj of res.data.data) {
Fore.push(parseInt(dataObj.intensity.forecast));
Act.push(parseInt(dataObj.intensity.actual));
setIsLoaded(true);
}
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
})
.catch((error) => {
setIsLoaded(true);
setChartData({
labels: Fore,
datasets: [
{
label: "Carbon Intensity Levels",
data: Act,
backgroundColor: "#F58A07",
borderWidth: 4
}
]
});
setError(error);
})
}, [])
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div> <ReactBootStrap.Spinner animation="border"/> </div>;
} else {
return (
<div className="App">
<h1></h1>
<div className="chart">
<Line
data={chartData}
options={{
responsive: true,
title: { text: "2020-09-01T15:30Z - 2020-09-10T17:00Z", display: true },
scales: {
yAxes: [
{
ticks: {
autoSkip: true,
maxTicksLimit: 100,
beginAtZero: true
},
gridLines: {
display: false
},
scaleLabel: {
display: true,
labelString: "Actual"
}
}
],
xAxes: [
{
gridLines: {
display: false
},
scaleLabel: {
display: true,
labelString: "Forecast"
}
}
]
}
}} />
</div>
</div>
);
}
}
export default MyComponent;
However now as I have caught the error it is dsiplaying on screen "Error: Cannot read property 'data' of undefined" so I think that my api data is maybe not being parsed properly but cant work out why
Try with this.
useEffect(() => {
let Fore = [];
let Act = [];
fetch('https://api.carbonintensity.org.uk/intensity/2020-09-01T15:30Z/2020-09-10T17:00Z')
.then(res => res.json())
.then(data => {
for (const dataObj of data.data) {
Fore.push(parseInt(dataObj.intensity.forecast));
Act.push(parseInt(dataObj.intensity.actual));
}
setIsLoaded(true);
})
.catch((error) => {
setIsLoaded(true);
setChartData({
labels: Fore,
datasets: [
{
label: "Carbon Intensity Levels",
data: Act,
backgroundColor: "#F58A07",
borderWidth: 4
}
]
});
setError(error);
})
}, [])
I created a chart in React app with react-chartjs-2. I have 2 items on the homepage: "chart1" and "chart2". When I click on the "chart1" - chart displays with no data. After second click, data are rendering correctly. Next I want to render "chart2", but after click on that item, "chart1" renders. After second click, data for "chart2" are rendering correctly.
Do you know what is the problem? I want to render every chart after first click.
Chart.js
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Line } from 'react-chartjs-2';
import { getChartData } from '../../actions/sensors';
const Chart = (props) => {
const [chartDataState, setChartDataState] = useState({});
const createChart = () => {
const id = props.match.params.id;
props.getChartData(id);
setChartDataState({
labels: props.chartData.map(data => {
const date = new Date(data.delivery_time).toLocaleDateString();
const time = new Date(data.delivery_time).toLocaleTimeString();
return `${time} | ${date}`;
}),
datasets: [
{
data: props.chartData.map(data => data.sensor_data),
fill: false,
backgroundColor: '#987316',
borderColor: 'rgba(152, 115, 22, 0.2)',
},
],
})
}
useEffect(() => {
createChart();
}, [])
return (
<div className="container">
<div className="head" style={{'marginBottom': '30px'}}>
<h2>Chart</h2>
<div className="line" style={{"width": "900px"}}></div>
</div>
<div className="chart">
<Line
data={chartDataState}
options={{
maintainAspectRatio: false,
legend: {
display: false,
}
}}
height={400}
/>
</div>
</div>
);
}
const mapStateToProps = state => ({
chartData: state.sensors.chartData,
})
export default connect(mapStateToProps, { getChartData })(Chart);
sensors (Redux action)
export const getChartData = id => (dispatch, getState) => {
const token = getState().auth.token;
const config = {
headers: {
'Content-Type': 'application/json',
'Authorization': `Token ${token}`
}
}
axios.get(`${baseURL}/sensors_data/list/${id}`, config)
.then(res => {
dispatch({
type: GET_CHART_DATA,
payload: res.data
})
})
.catch(err => console.log(err))
}
sensors (Redux reducer)
import { GET_CHART_DATA } from '../actions/types';
const initialState = {
chartData: [],
}
export default function(state = initialState, action) {
switch(action.type) {
case GET_CHART_DATA:
return {
...state,
chartData: action.payload
}
default:
return state;
}
When you first click, you trigger a request for the data, but your component does not wait for that data before rendering.
I would recommend adding a condition to your return. This is how I would have done it without Redux (I've never used Redux)
return (
<div className="container">
<div className="head" style={{'marginBottom': '30px'}}>
<h2>Chart</h2>
<div className="line" style={{"width": "900px"}}></div>
</div>
<div className="chart">
{chartDataState.length > 0 ?
<Line
data={chartDataState}
options={{
maintainAspectRatio: false,
legend: {
display: false,
}
}}
height={400}
/>
: <div>Loading...</div>}
</div>
</div>
);
Basically: you don't want your component to render the chart before the data is returned.
I built a webapp where the user enters some text and we get some scores and create a barchart based on those scores. However I want to create a grouped barchart now for comparison. Suppose the user change something in the text and we get new score so i want to show this new scores in the bar chart with the previous score. It should compare only the current value and the previous value and not go farther than that. I tried to code this but got error like
codesandbox
ReferenceError: Cannot access 'current' before initialization
Editor.js I'm just pasting important parts of the code since it's a big file
import React, { Component, useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { Button, Tabs, Tab, Modal } from "react-bootstrap";
import BarChart from "../barCharts";
const style2 = {
flex: 1,
textAlign: "center"
};
class MainText extends Component {
state = {
analysis: {
tonal: [],
},
showButton: false
};
Analysis = async () => {
const { enteredText } = this.state;
const body = { snippetdesc: enteredText };
const stringifiedBody = JSON.stringify(body);
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: stringifiedBody
};
const url = `http://localhost:5000/api/apicall`;
try {
const response = await fetch(url, options);
const result = await response.json();
const {
...tonalAnalysis
} = result.scores;
const tonalArray = Object.entries(tonalAnalysis).reduce(
(carry, [tone, value]) => [
...carry,
{ tone: tone.toLowerCase(), value: parseInt(value) }
],
[]
);
this.setState({
analysis: { ...this.state.analysis, tonal: tonalArray },
showButton: true
});
} catch (error) {
console.error("error when performing sentiment analysis");
}
};
render() {
const series = this.state.analysis.tonal.map((tonal) => tonal.value);
const prev=current;
const current = series; //error here
return (
<div>
//<textarea>
//analysis button
<Button
className={this.state.showButton ? "showButton" : "hideButton"} //.showButton{visibility: visible}; .hideButton{visibility: hidden}
onClick={this.showPieModal}
>
Show BarChart Modal
</Button>
{this.state.showPieModal && (
<Modal show={this.state.showPieModal} onHide={this.handleClosePie}>
<Modal.Header closeButton>
<Modal.Title>Analysis</Modal.Title>
</Modal.Header>
<Modal.Body className="pieModal">
<BarChart data={current} prev={prev}/>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.handleClosePie}>
Close
</Button>
</Modal.Footer>
</Modal>
)}
</div>
);
}
}
export default MainText;
BarChart.js
import React from "react";
import { Bar } from "react-chartjs-2";
import "./App.css";
class BarChart extends React.Component {
constructor(props) {
super(props);
console.log("props" + JSON.stringify(this.props));
this.state = {
data: {
labels: [
"January",
"February",
"March",
],
datasets: [
{
label: "My First dataset",
backgroundColor: "rgba(255,99,132,0.2)",
borderColor: "rgba(255,99,132,1)",
borderWidth: 1,
//stack: 1,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
data: this.props.data
},
{
label: "My second dataset",
backgroundColor: "rgba(155,231,91,0.2)",
borderColor: "rgba(255,99,132,1)",
borderWidth: 1,
//stack: 1,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
data: this.props.prev
}
]
}
};
}
render() {
const options = {
responsive: true,
legend: {
display: false
},
type: "bar",
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
};
return (
<Bar
data={this.state.data}
options={options}
width={null}
height={null}
options={options}
/>
);
}
}
export default BarChart;
Error explains it, you are trying to access a constant before you initialize it. const current = series; is after const prev = current;.
But the problem is you are trying to render the previous values without storing them in state. What if your component re-renders? The previous data will be lost.
You want to keep the previous values in state:
state = {
analysis: {
tonal: [],
},
prevAnalysis: {
tonal: [],
},
showButton: false,
};
Update when you receive new data:
const currAnalysis = // clone this.state.analysis
this.setState({
prevAnalysis: currAnalysis,
analysis: { ...this.state.analysis, tonal: tonalArray },
showButton: true
});
Then use the prevAnalysis in your component as prev. You don't need this
const prev = current;
const current = series;
Generally, everything that is not saved in state will be used for the current render, whenever you want to keep previous values etc. in memory, you store them in state.
You have error in your code here:
render() {
const series = this.state.analysis.tonal.map((tonal) => tonal.value);
const prev=current;
const current = series; //error here
Your code trying to access value current, and after that you are declaring it using const.
I'm trying to update state under a promise. But the component is going over and over again infinitely until it max out the heap size. I don't know what I'm missing here.
This is my code
import {useDropzone} from 'react-dropzone';
import File from './File'
import parser from 'subtitles-parser'
const baseStyle = {
flex: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: '20px',
borderWidth: 2,
borderRadius: 2,
borderColor: '#eeeeee',
borderStyle: 'dashed',
backgroundColor: '#fafafa',
color: '#bdbdbd',
outline: 'none',
transition: 'border .24s ease-in-out'
};
const activeStyle = {
borderColor: '#2196f3'
};
const acceptStyle = {
borderColor: '#00e676'
};
const rejectStyle = {
borderColor: '#ff1744'
};
function Drag(props) {
const {
getRootProps,
getInputProps,
isDragActive,
isDragAccept,
isDragReject,
acceptedFiles
} = useDropzone();
const style = useMemo(() => ({
...baseStyle,
...(isDragActive ? activeStyle : {}),
...(isDragAccept ? acceptStyle : {}),
...(isDragReject ? rejectStyle : {})
}), [
isDragActive,
isDragReject
]);
const [data, setData] = useState(null)
if(acceptedFiles.length === 1){
const readUploadedFileAsText = (acceptedFiles) => {
const temporaryFileReader = new FileReader();
return new Promise((resolve, reject) => {
temporaryFileReader.onerror = () => {
temporaryFileReader.abort();
reject(new DOMException("Problem parsing input file."));
};
temporaryFileReader.onload = () => {
resolve(parser.fromSrt(temporaryFileReader.result));
};
temporaryFileReader.readAsText(acceptedFiles);
});
};
let file = acceptedFiles[0]
readUploadedFileAsText(file)
.then(res => {
setData({
data: res
})
})
}
return (
<div className="container">
<div {...getRootProps({style})}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
<File file={data} />
{console.log(data)}
</div>
);
}
export default Drag
Let me know if I have to use any lifecycle methods. I tried using componentDidMount and componentWillReceiveProps but both didn't work for me or I hadn't done them in right way.
Take your readUploadedFileAsText function outside of the if statement it is in. Then you can add your if and function call to React.useEffect which will call the function on initial load, but not on every subsequent re-render.
function Drag(props) {
const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject, acceptedFiles } = useDropzone();
const style = useMemo(
() => ({
...baseStyle,
...(isDragActive ? activeStyle : {}),
...(isDragAccept ? acceptStyle : {}),
...(isDragReject ? rejectStyle : {})
}),
[isDragActive, isDragReject]
);
const [data, setData] = useState(null);
const readUploadedFileAsText = acceptedFiles => {
const temporaryFileReader = new FileReader();
return new Promise((resolve, reject) => {
temporaryFileReader.onerror = () => {
temporaryFileReader.abort();
reject(new DOMException('Problem parsing input file.'));
};
temporaryFileReader.onload = () => {
resolve(parser.fromSrt(temporaryFileReader.result));
};
temporaryFileReader.readAsText(acceptedFiles);
});
};
React.useEffect(() => {
if (acceptedFiles.length === 1) {
let file = acceptedFiles[0];
readUploadedFileAsText(file).then(res => {
setData({
data: res
});
});
}
}, [acceptedFiles]);
return (
<div className='container'>
<div {...getRootProps({ style })}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
<File file={data} />
{console.log(data)}
</div>
);
}