Reputation: 23
I am currently creating a front-end app in order to display some results of ML, and using React + Recharts. My version of React is 16.11.0, and version of recharts is 1.8.5.
Every time I update my data (after every iteration), I want to update the chart. So it first displays 30 points, that is the comportment intended, but when I add my 30 new points, pushing the array used to display data to 60, it looks like my component is not re-rendering, and only displays 30 points.
All the logic seems fine : on my main page side, the aggregation of data is correctly done, and the component receives the updated data correctly. But my chart still won't update.
Maybe am I doing things wrongly with the hooks, since I'm still a beginner. Thanks for your help.
The main page code, with the part of "simulateLocal" managing the data :
import React, { useEffect, useState } from "react";
import { TextField, Button } from "@material-ui/core/";
import Grid from "@material-ui/core/Grid";
import CardContent from "@material-ui/core/CardContent";
import Box from "@material-ui/core/Box";
import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/core/styles";
import Chart from "./Charts";
let results = []
let params = [];
const useStyles = makeStyles({
root: {
maxWidth: 400
},
title: {
fontSize: 20
}
});
async function initializeState(e, contract, accounts) {
await fetch("/init")
.then(res => res.json())
.then(data => {
console.log(data);
})
.catch(console.log);
}
async function simulateLocal(e, contract, accounts, setCount, count, setResult, result) {
//e.preventDefault();
console.log("Local stochastic gradient descent");
await contract.methods.resetState().send({ gas: 5000000, from: accounts[0] });
await fetch("/run_stage")
.then(res => res.json())
.then(data => {
params = data;
})
.catch(console.log);
console.log(contract);
await fetch("/get_results")
.then(res => res.json())
.then(data => {
if(results.length == 0 ){
results = { results : []}
}
data.results.forEach(result => {
result.name = (parseInt(result.name, 10) + results.results.length).toString();
})
results.results.push(...data.results);
setResult(results)
});
console.log(results);
setCount(count + 1);
}
async function simulateFederated(e, contract, accounts) {
e.preventDefault();
console.log("Federation happening.");
console.log(params);
for (var i = 0; i < 4; i++) {
await contract.methods
.store_params(params.weights[i])
.send({ from: accounts[i + 1], gas: 5000000 });
}
const aggres = await contract.methods
.run_agg()
.send({ gas: 5000000, from: accounts[0] });
console.log("runagg:" + aggres);
const weight_res = await contract.methods
.read_params()
.call({ gas: 500000000, from: accounts[0] });
console.log(weight_res);
for (var i = 0; i < 4; i++) {
await contract.methods
.store_params(params.biases[i])
.send({ from: accounts[i + 1], gas: 5000000 });
}
const aggres2 = await contract.methods
.run_agg()
.send({ gas: 500000000, from: accounts[0] });
console.log("runagg:" + aggres2);
const bias_res = await contract.methods
.read_params()
.call({ gas: 500000000, from: accounts[0] });
console.log(bias_res);
let postres = [];
await fetch("/post_params", {
method: "POST",
body: JSON.stringify({
weights: JSON.stringify(weight_res),
biases: JSON.stringify(bias_res)
})
})
.then(res => res.json())
.then(data => {
postres = data;
});
console.log(postres);
}
const ClientField = props => {
const [count, setCount] = useState(0);
const [result, setResult] = useState(results);
const classes = useStyles();
return (
<Box border={0} borderColor="grey.500">
<CardContent>
<Typography className={classes.header} variant="h6">
Simulation Panel
</Typography>
</CardContent>
<CardContent>
<Button
variant="contained"
size="small"
style={{
maxWidth: "100px",
maxHeight: "30px",
minWidth: "100px",
minHeight: "30px",
margin: "10px"
}}
onClick={e =>
simulateLocal(e, props.instance, props.accounts, setCount, count, setResult, result)
}
>
Run Local
</Button>
<Button
variant="contained"
size="small"
style={{
maxWidth: "100px",
maxHeight: "30px",
minWidth: "100px",
minHeight: "30px",
margin: "10px"
}}
onClick={e => simulateFederated(e, props.instance, props.accounts)}
>
Federate
</Button>
<Button
variant="contained"
size="small"
style={{
maxWidth: "100px",
maxHeight: "30px",
minWidth: "100px",
minHeight: "30px",
margin: "10px"
}}
onClick={e => initializeState(e, props.instance, props.accounts)}
>
Initialize
</Button>
</CardContent>
<CardContent
style={{
margin: "auto",
minHeight: "300px",
maxHeight: "300px",
maxWidth: "400px"
}}
>
<Chart data={result} count={count}></Chart>
</CardContent>
</Box>
);
};
export default ClientField;
And the component Charts, using the Recharts lib.
import React, { useState, useEffect, useDeepCompareEffect } from "react";
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend
} from "recharts";
const Results = ({ data, count }) => {
const [result, setResult] = useState();
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
console.log("charts props:", data.results);
setResult(data.results)
console.log("results updated")
});
return (
<LineChart
width={500}
height={300}
data={result}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5
}}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip />
<Legend />
<Line
type="monotone"
dataKey="uv"
stroke="#8884d8"
activeDot={{ r: 8 }}
/>
</LineChart>
);
};
export default Results;
Upvotes: 2
Views: 5256
Reputation: 1241
results.results.push()
is performed in simulateLocal
, the result
reference remains the same and setResult(results)
doesn't trigger rerender. You need to pass a new array, like this: setResult([...results])
.Results
component doesn't need useEffect
hook, you can pass data.results
prop directly into LineChart
:const Results = ({ data, count }) => {
return (
<LineChart data={data.result}>
{/* ... */}
</LineChart>
);
};
Upvotes: 1
Reputation: 6899
In hooks if you update state with same value your component doesn't re-render. If you want to re-render then you need to create new array or object.
if data.results is array then do
setResults([...data.results]);
if data.results is object then do
setResults({...data.results});
Upvotes: 1