My goal is to use the button on the page to open a websocket connection, subscribe to the ticker feed, then update each list item's price based on the ID of the list item. Currently I have a list that maps through the initial API call's response and saves each object's ID to an array, which in turn is used to build a <li>
for each ID. This creates 96 list items. I have also gotten the price to update live via a <p>
element in each <li>
.
I am having trouble targeting the price for just the matching row ID to the incoming data object's ID so that only that matching row is re-rendered when it gets a match. Below is my code:
ProductRow.js
import React from 'react';
export default function ProductRow(props) {
return <li key={props.id}><p>{ props.id }</p><p>{props.price}</p></li>;
}
WatchList.js
import React, { useState, useEffect, useRef } from "react";
import { Button } from 'react-bootstrap';
import ProductRow from "./ProductRow";
export default function WatchList() {
const [currencies, setcurrencies] = useState([]);
const product_ids = currencies.map((cur) => cur.id);
const [price, setprice] = useState("0.00");
const [isToggle, setToggle] = useState();
const ws = useRef(null);
let first = useRef(false);
const url = "https://api.pro.coinbase.com";
useEffect(() => {
ws.current = new WebSocket("wss://ws-feed.pro.coinbase.com");
let pairs = [];
const apiCall = async () => {
await fetch(url + "/products")
.then((res) => res.json())
.then((data) => (pairs = data));
let filtered = pairs.filter((pair) => {
if (pair.quote_currency === "USD") {
return pair;
}
});
filtered = filtered.sort((a, b) => {
if (a.base_currency < b.base_currency) {
return -1;
}
if (a.base_currency > b.base_currency) {
return 1;
}
return 0;
});
setcurrencies(filtered);
first.current = true;
};
apiCall();
}, []);
useEffect(() => {
ws.current.onmessage = (e) => {
if (!first.current) {
return;
}
let data = JSON.parse(e.data);
if (data.type !== "ticker") {
return;
}
setprice(data.price);
console.log(data.product_id, price);
};
}, [price]);
const handleToggleClick = (e) => {
if (!isToggle) {
let msg = {
type: "subscribe",
product_ids: product_ids,
channels: ["ticker"]
};
let jsonMsg = JSON.stringify(msg);
ws.current.send(jsonMsg);
setToggle(true);
console.log('Toggled On');
}
else {
let unsubMsg = {
type: "unsubscribe",
product_ids: product_ids,
channels: ["ticker"]
};
let unsub = JSON.stringify(unsubMsg);
ws.current.send(unsub);
setToggle(false);
console.log('Toggled Off');
}
};
return (
<div className="container">
<Button onClick={handleToggleClick}><p className="mb-0">Toggle</p></Button>
<ul>
{currencies.map((cur) => {
return <ProductRow id={cur.id} price={price}></ProductRow>
})}
</ul>
</div>
);
}
App.js
import React from "react";
import WatchList from "./components/Watchlist";
import "./scss/App.scss";
export default class App extends React.Component {
render() {
return (
<WatchList></WatchList>
)
}
}