我目前正在创建一个前端应用程序,以显示ML的一些结果,并使用React + Recharts。我的 React 版本是 16.11.0,重图版本是 1.8.5。
每次更新数据(每次迭代后)时,我都想更新图表。因此,它首先显示 30 个点,这是预期的特征,但是当我添加 30 个新点,将用于显示数据的数组推到 60 时,看起来我的组件没有重新渲染,只显示 30 个点。
所有的逻辑似乎都很好:在我的主页端,数据的聚合正确完成,组件正确接收更新的数据。但我的图表仍然不会更新。
也许我在钩子上做错了事情,因为我仍然是初学者。感谢您的帮助。
主页代码,其中"模拟本地"部分管理数据:
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;
和组件图表,使用重图库。
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;
在钩子中,如果您使用相同的值更新状态,则组件不会重新渲染。 如果要重新渲染,则需要创建新的数组或对象。
如果 data.results 是数组,则执行
setResults([...data.results]);
如果 data.results 是对象,则执行
setResults({...data.results});
- React 组件将状态和 prop 与以前的值进行比较,并在更改时重新渲染。标量变量(字符串、数字)按值比较,对象(包括数组)按引用比较。也就是说,当
results.results.push()
在simulateLocal
中执行时,result
引用保持不变,setResult(results)
不会触发重新渲染。您需要传递一个新数组,如下所示:setResult([...results])
. - 你的
Results
组件不需要useEffect
钩子,你可以data.results
prop 直接传递给LineChart
:
const Results = ({ data, count }) => {
return (
<LineChart data={data.result}>
{/* ... */}
</LineChart>
);
};