I want to close event source after user logout. My codes are below. When user logging out push notifications are still coming. My questions is how to close event source?
const eventSource = new EventSource(`${environment.notificationUrl}/subscribe?
token=${token}`);
if (!token) {
eventSource.removeAllEventListeners()
eventSource.close()
return;
}
eventSource.addEventListener('message', (event: any) => {
---- some codes here ----
}
Technically, event listeners should be subscribed inside useEffect and keep tracking when user logged out.
const NotificationSubscriptionManager = () => {
const eventSource =
new EventSource(`${environment.notificationUrl}/subscribe?
token=${token}`);
const subScribeToNotifications = () => {
eventSource.addEventListener("message", (event: any) => {
/// ---- some codes here ----
});
};
const unsubScribeToNotifications = () => {
eventSource.removeAllEventListeners();
eventSource.close();
};
useEffect(() => {
if (!token) {
return unsubScribeToNotifications();
}
// Subscribe to notification events
subScribeToNotifications();
// Cancel all subscription when component unmount to avoid
// memory leak
return () => {
unsubScribeToNotifications();
};
}, [token]);
};
Related
Does the following code create duplicate on.message listeners ?
when i execute on.message once, the payloads array takes it's initial value each time
export default function ProjectChat() {
const { id: projectId } = useParams()
const [payloads, setPayloads] = useState([])
const [messageBody, setMessageBody] = useState('')
const [webSocket, setWebSocket] = useState(null)
// Create the connection
useEffect(() => {
const url = `ws://localhost:3000/ws/${projectId}`;
const ws = new WebSocket(url);
setWebSocket(ws)
return () => ws.close()
}, [])
useEffect(() => {
if (webSocket !== null) {
webSocket.onmessage = e => {
const payload = JSON.parse(e.data);
const allMessages = [...payloads, payload]
setPayloads(allMessages)
}
}
}, [webSocket, payloads])
return <p>jsx</p>
}
You can't create duplicate listeners with on.message. Every time you define on.message, you're over-writing the previous listener.
The addListener is the method which can lead to multiple listeners. In that case, you would want to run removeEventListener() on the listener you created inside your component's unmount phase return () => removeEvenListener(...).
I am trying to optimize my react code by fixing any memory leaks. For this i am using createAsyncThunk canceling-while-running.
I am successful in implementing this using useEffect where I dispatch a reducer to request,
when the component mounts and i could automatically trigger abort() signal when the component unmounts using the return of useEffect. Refer the below code:
useEffect(() => {
const promise = dispatch(getIssuedBooks())
return promise.abort()
}, []);
But i have other reducers which get dispatched on onClick events. Refer the code below:
const handleRequest = (id) => {
dispatch(requestBook(id))
}
My problem is when component unmounts how do i abort from this request. I tried some things but it did not work out. Please help. Thanks in advance.
const controller = new AbortController();
const signal = controller.signal;
const url = "video.mp4";
const downloadBtn = document.querySelector(".download");
const abortBtn = document.querySelector(".abort");
downloadBtn.addEventListener("click", fetchVideo);
abortBtn.addEventListener("click", () => {
controller.abort();
console.log("Download aborted");
});
function fetchVideo() {
fetch(url, { signal })
.then((response) => {
console.log("Download complete", response);
})
.catch((err) => {
console.error(`Download error: ${err.message}`);
});
}
I made an application that can make agora voice calls with React native. When clicking leave on the screen, the below agoraEnd function works correctly. I also want this function to work when the phone's back button is pressed. That's why I prepared the useFocusEffet below. But when I press the back button during a voice call, I get the following error. Why is this function not working correctly? How can I fix this problem.
Possible Unhandled Promise Rejection (id: 2): TypeError: engine.leaveChannel is not a function. (In 'engine.leaveChannel()', 'engine.leaveChannel' is undefined)
const agoraEnd = async () => {
await engine.leaveChannel()
await engine.destroy()
navigation.goBack();
};
useFocusEffect(
useCallback(() => {
const backHandler = BackHandler.addEventListener(
'hardwareBackPress',
agoraEnd,
);
return () => backHandler.remove();
}, []),
)
Based on your code, you don't return anything from back handler (though it expects boolean, where true when you handled back press and false when you didn't and want to continue default behaviour (usually exiting the app))
You can just do:
useFocusEffect(
useCallback(() => {
const backHandler = BackHandler.addEventListener(
'hardwareBackPress',
() => agoraEnd(),
);
return () => backHandler.remove();
}, []),
)
if you don't want to wait for agoraEnd to finish.
So in my case I used BackHandler.exitApp(); to do async stuff:
const handleBackButtonAsync = async () => {
// do some await stuff
if (something) {
// the back button event is handled;
} else {
// THE MAGIC:
// instead of returning `false` in `handleBackButton`
// to not prevent default logic, you exit app explicitly
BackHandler.exitApp();
}
}
const handleBackButton = () => {
handleBackButtonAsync();
return true; // always "handle" the back button event to not exit app
}
useEffect(() => {
BackHandler.addEventListener("hardwareBackPress", handleBackButton);
return () =>
BackHandler.removeEventListener("hardwareBackPress", handleBackButton);
}, []);
I'm struggling to put a state inside a function without parameters as a reference(if I understand it correctly).
I have an event class that has methods close and open. By default, these methods don't do anything(besides throwing an error about missing functionality) until you assign them callbacks via assignOpenCallback and assignCloseCallback where you define their behaviour from "outside".
export class Event {
constructor({ id, coordinates, content }: EventConstructor) {
this.id = id;
this.coordinates = coordinates;
this.content = content;
}
public id: EventID;
public coordinates: EventCoordinates;
public content: EventData;
public isSelected: boolean = false;
public clickAction: ClickEventAction = () => {
if (this.isSelected) return this.close();
return this.open();
}
//Defining of methods from outside
public assignCloseCallback = (callback: () => void) => this.close = () => callback();
public assignOpenCallback = (callback: () => void) => this.open = () => callback();
public close: CloseEventAction = () => { // <--- This
throw new Error("Close event functionality isn't assigned.");
}
public open: OpenEventAction = () => { // <--- And this
throw new Error("Open event functionality isn't assigned.");
}
}
I use the state selectedEvent which is keeping the selected event by a user and sending this state to other components.
//State of all available events
const [events, setEvents] = useState<Array<Event>>([]);
//State of selected event
const [selectedEvent, setSelectedEvent] = useState<SelectedEvent>(null);
I define the behaviour of mentioned methods in the useEffect hook with empty array dependency which imitates API data fetching to describe logic when the user clicks on the event and there I use selectedEvent state for a case when the event was changed without actually closing the event(while event open and the user has been opened another event).
const closeEvent = (event: Event) => {
console.log(selectedEvent);
setSelectedEvent(null);
event.isSelected = false;
}
const openEvent = (event: Event) => {
console.log(selectedEvent)
if (selectedEvent) selectedEvent.close();
setSelectedEvent(event);
event.isSelected = true;
}
useEffect(() => {
const loadedEvents = fakeResponse.events;
const events = loadedEvents.map(loadedEvent => {
const { id, content, coordinates } = loadedEvent;
const event = new Event({ id, content, coordinates });
event.assignCloseCallback(() => closeEvent(event));
event.assignOpenCallback(() => openEvent(event));
return event;
});
setEvents(events);
}, []);
And the problem is that these functions always refer to the initial value of the state which is null regardless of the real state.
I'm a little bit frustrated because I even don't know where the problem is.
I'm a novice in React so any advice would be helpfull.
I'm using websockets from this library: https://www.npmjs.com/package/websocket
This is a function in React.ts 17 that successfully retrieves the data from the server, but fails to return the values of the function itself.
const recieveMessages = () => {
client.onmessage = (message: any) => {
const dataFromServer = JSON.parse(message.data)
console.log(dataFromServer) //This successfully logs the data
return dataFromServer //This is always returned as undefined
}
//I've tried a few versions with the return statement here without any success.
}
How can I make the recieveMessages function return the data from the client.onmessage function?
Update: I'm trying to seperate all the logic into a seperate React hook(useWebSocket) which currently looks like this:
import { w3cwebsocket } from 'websocket'
export const useWebSocket = () => {
const client = new w3cwebsocket('ws://127.0.0.1:8000')
const connectToServer = () => {
client.onopen = () => { console.log('Websocket client connected') }
}
const recieveMessages = () => {
client.onmessage = (message: any) => {
const dataFromServer = JSON.parse(message.data)
console.log('reply from server: ', dataFromServer)
return dataFromServer
}
}
const sendMessage = (message: any) => {
client.send(JSON.stringify('lol'))
}
const onCloseServer = (event: CloseEvent) => {
console.log(event)
}
return {
connectToServer,
recieveMessages,
sendMessage,
onCloseServer
}
}
I'm then trying to run the function inside the useEffect in a separate component like this:
The desire is to set the following data inside the local state of this component.
useEffect(() => {
recieveMessages()
setState(recieveMessages()) //This is always undefined
}, [])
This calls for a useState hook inside your useWebSocket hook.
Whenever client.onmessage receives a message, store that message inside of a state and update it every time another message is received.
Then return the state from the hook.
import { useState } from 'react'
import { w3cwebsocket } from 'websocket'
export const useWebSocket = () => {
const [receivedMessage, setReceivedMessage] = useState('')
const client = new w3cwebsocket('ws://127.0.0.1:8000')
client.onmessage = (message: any) => {
const dataFromServer = JSON.parse(message.data)
setReceivedMessage(dataFromServer)
}
const sendMessage = (message: any) => {
client.send(JSON.stringify(message))
}
return {
receivedMessage,
sendMessage,
}
}
Then implement it like so. The receivedMessage value is the state that will be updated and can be monitered with a useEffect hook to do something whenever a message has been received.
const { receivedMessage, sendMessage } = useWebSocket()
useEffect(() => {
if (receivedMessage !== '') {
// Do something whenever the received message is changed.
sendMessage('Received you loud and clear')
}
}, [receivedMessage])