I have a useEffect that subscribes to websocket at startup and adds the data that comes from the server to the operationList list. This list is displayed on the webpage. This is a list of operations performed by construction equipment, which I receive online and could not chage on a backend.
useEffect(() => {
const ws = new WebSocket('ws://127.0.0.1:8000/ws/')
ws.addEventListener('message', (e) => {
const response = JSON.parse(e.data)
setOperationsList(operationsList => [
response,
...operationsList,
]);
})
ws.onopen = () => {
ws.send(JSON.stringify({
action: "subscribe_to_operations_activity",
request_id: new Date().getTime(),
}))
}
}, [])
In the same component I have a state, which stores information about the id of the equipment, the report on which is currently displayed on the screen.
const [activeID, setActiveID] = useState(false)
I need to add to the operationsList not all the data that comes in the server, as it is done now response = JSON.parse(e.data). Instead, I need to check the condition response.id == activeID, and to add to operationsList only those data that satisfy the condition. I can't add activeID to the list of useEffect dependencies because in this case, a new WebSocket subscription will be created every time the activeID changes. I understand that most likely I don't understand some basic things, so I would be grateful for any advice
Added the activeID path:
In my component
const [activeID, setActiveID] = useState(false)
const getElementStar = (newID) => {
setActiveID(newID)
}
Then I pass it to child component as props:
<LeftMenu
...
getElementStar={getElementStar}
...
/>
And finally in child component
const getKey = () => {
const newID = props.item.id
props.getElementStar(newID)
}
return (
<ListItemButton sx={{ pl: 4 }} key={props.index} onClick={getKey}>
<ListItemIcon>
{ props.star === props.index ? <Star/> : <StarBorder/> }
</ListItemIcon>
<ListItemText primary={props.item.name} />
</ListItemButton>
);
How activeID is supposed to change?
Basically, storing something in a state implies that the component need a re-render when this value changes. Which does not seem to be your case here?
So I would suggest to have your activeID stored as a ref
const activeID = useRef('[my id]')
useEffect(() => {
const ws = new WebSocket('ws://127.0.0.1:8000/ws/')
ws.addEventListener('message', (e) => {
const response = JSON.parse(e.data)
setOperationsList(operationsList => [
...response.filter(o => o.id === activeID.current),
...operationsList,
]);
})
ws.onopen = () => {
ws.send(JSON.stringify({
action: "subscribe_to_operations_activity",
request_id: new Date().getTime(),
}))
}
}, [])
If activeID needs to change somehow, you can have this by doing
useEffect(() => {
activeID.current = [my new value]
}, [triggers that could change activeId (maybe a prop?)])
=== EDIT ===
So I assume changing activeID must trigger a re-render to change your <Star > component depending on current activeID right? :)
So I would suggest 2 differents things. Either with the ref, so something like that
const [activeID, setActiveID] = useState();
const activeIDRef = useRef()
on your useEffect having
ws.addEventListener('message', (e) => {
const response = JSON.parse(e.data)
setOperationsList(operationsList => [
...response.filter(o => o.id === activeIDRef.current),
...operationsList,
]);
})
and have a second useEffect to change ref when activeID changes
useEffect(() => {
activeIDRef.current = activeID
}, [activeID])
OR
Maybe it would be easier to keep on storing everything in your operationsList BUT having it filtered when displaying values. If you need it in a state, like this
const [operationsListFiltered, setOperationsListFiltered] = useState([]);
useEffect(() => {
setOperationsListFiltered(operationsList.filter(o => o.id === activeId))
}, [activeId, operationsList])
Or directly as variable
const operationsListFiltered = useMemo(() => operationsList.filter(o => o.id === activeId), [activeId, operationsList]);
This way, either your activeID OR your operationLists from your websocket changes, your operationsListFiltered will be updated
You can try running a loop with a condition that, once true, will then push what you want to push to the specific list. That will only work if the data you are receiving from the back-end is a type of array.
let dataList = []
const loopData = () => {
e.data.forEach((obj) => {
if (obj.id === activeID) {
dataList.push(obj)
}
}
}
useEffect(() => loopData(), []);
And then
setOperationsList(...dataList)