Reputation: 131
i got a problem while working with mui data-grid and socket.io. I load initial data from an api which works fine. But when I want to update the table with data from an websocket is only one element added and overwritten on every change. The code is the following:
Table:
const [tableData, setTableData] = useState(props.tableData);
useEffect(() => {
setTableData(props.tableData);
}, [props.tableData]);
return (<div key="tradingTableGrid" style={{ height: 450, width: "100%" }}>
<DataGrid
key={"TradingTable"}
rows={Object.values(tableData)}
columns={tableHead}
rowHeight={30}
pageSize={10}
onRowClick={(row) => openModal(row)}
/>
</div>);
The component where the initial data are loaded and the websocket update is called is the following (i have only added the essential code):
const [data, setData] = useState({});
const [loadData, setLoadData] = useState(false);
const [socketActive, setSocketActive] = useState(false);
// initial data loading from api
const handleResponse = (response) => {
var result = {};
response.forEach((prop) => {
result = {
...result,
[prop.order_id]: {
id: prop.order_id,
orderId: prop.order_id,
price: prop.price.toString(),
volume: prop.max_amount_currency_to_trade,
minVolume: prop.min_amount_currency_to_trade,
buyVolume: prop.max_volume_currency_to_pay + " €",
minBuyVolume: prop.min_volume_currency_to_pay + " €",
user: prop.trading_partner_information.username,
},
};
});
setData(result);
};
// add from websocket
const addOrder = (order) => {
if (
order.trading_pair === "btceur" &&
order.order_type === "buy" &&
(order.payment_option === "1" || order.payment_option === "3")
) {
if (typeof data[order.order_id] === "undefined") {
console.log(data);
setData({
...data,
[order.order_id]: {
id: order.order_id,
orderId: order.order_id,
price: order.price.toString(),
volume: order.amount,
minVolume: order.min_amount,
buyVolume: Number(order.volume).toFixed(2) + " €",
minBuyVolume: Number(order.min_amount * order.price).toFixed(2) + " €",
user: "not set through socket",
},
});
}
}
};
useEffect(() => {
const loadData = () => {
setLoadData(true);
axios
.get(process.env.REACT_APP_BITCOIN_SERVICE + "/order/book?type=buy")
.then((response) => handleResponse(response.data.orders))
.catch((exception) => console.error(exception))
.finally(() => setLoadData(false));
};
if (Object.keys(data).length === 0 && socketActive === false) {
loadData();
}
const socket = () => {
const websocket = io("https://ws-mig.bitcoin.de:443/market", {
path: "/socket.io/1",
});
websocket.on("connect", function () {
setSocketActive(true);
});
websocket.on("remove_order", function (order) {
removeOrder(order);
});
websocket.on("add_order", function (order) {
addOrder(order);
});
websocket.on("disconnect", function () {
setSocketActive(false);
});
};
if (socketActive === false && Object.keys(data).length !== 0) {
socket();
}
}, [data, socketActive]);
return (
<Table
tableHeaderColor="info"
tableHead={tableHead}
tableData={data}
type="buy"
/>
);
Does anyone has an idea why on the update throught the socket only one element in the data is added and on every next call overwritten?
Upvotes: 2
Views: 1105
Reputation: 5101
The reason is that the socket handlers are using the state setters from the first render.
as an example take addOrder
, you create that function within the context of a specific data value, when you call setData({...data
it will always use the initial state for data. (to understand why see How do JavaScript closures work? and also A Complete Guide to useEffect from facebook's react developer Dan Abramov)
what you can do instead is use the functional update syntax that lets you pass a function to setData:
setData( data => ({...data,
this ensures the last stored value is always used.
see the react docs on useState Functional updates
Upvotes: 2