React Hooks: state not updating when called inside Socket.io handler - node.js

const [questionIndex, setQuestionIndex] = useState(0);
...
socket.on('next', () => {
console.log('hey')
setQuestionIndex(questionIndex + 1);
});
...
useEffect(() => {
console.log(questionIndex);
}, [questionIndex]);
I have a page that connects to a websocket using Socket.io. I am attempting to update the value of my questionIndex state variable when I receive a 'next' message from the socket. I seem to be receiving the message because 'hey' is printed, but questionIndex only updates after the first 'hey'. Afterwards, hey is printed, but questionIndex is not (I use the useEffect to print questionIndex when it is updated). Do you guys see anything wrong?

I was also facing this issue. Actually, we should not rely on values from state while updation.
right way to do this is
setQuestionIndex(QuestionIndex=>QuestioIndex+1)

For those wondering, it looks like the socket.on('next') function handler was using the original value of questionIndex (which was 0) every time. It seems like the function handler compiled this variable when it was bound rather than reading it at runtime. Not sure where in the documentation it specifies this behavior.
My solution was to change the function handler as such:
socket.on('next', (newIndex) => {
console.log('hey')
setQuestionIndex(newIndex);
});
This solves the problem by providing the value to set questionIndex to (rather than reading it upon receiving a 'next' event).

It looks like you open new socket every render and never disconnect it.
Try to place socket.on inside useEffect and the return value of that useEffect will be function that disconnect the socket.
useEffect(() => {
socket.on('next', () => {
console.log('hey')
setQuestionIndex(questionIndex + 1);
});
return () => socket.disconnect();
}, [questionIndex]);```

Related

How to stop memory leak in useEffect react

Hello I am facing a memory leak in my useEffect and I am getting this error "Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function."
I am trying to update my messages state when I emit the message from the backend.
my code looks like this
useEffect(() => {
socket.on("message", data => {
setMessages(prevVal => [...prevVal, {
author: data.id,
text: data.text
}]);
});
}, [socket]);
Please help and Thank You!
This happens if your component has already unmounted by the time the set state call is made, and this can happen because you're calling an asynchronous function socket.on which uses a callback.
Whenever you have these asynchronous side effects that may try to alter the state of your component, you should use a cleanup pattern.
So for example (given a socket.io socket)
useEffect(() => {
const onMessage = data => {
setMessages(prevVal => [...prevVal, {
author: data.id,
text: data.text
}]);
};
// Register listener
socket.on("message", onMessage);
// On cleanup, remove the attached listener
return () => socket.off("message", onMessage)
}, [socket]);
The cleanup function returned by useEffect will run when the component is unmounted, causing the set state call to be skipped.

Remove Socket.IO emitter on disconnect from EventEmitter attatched event array of functions

I have EventEmitter and socket.io working in tandem and they work as I am expecting them to work. I have an object, that contains data, that data can randomly change at any point in time, when the data changes, it fires an EventEmitter.emit with the updated data.
In another file I have an EventEmitter.on which captures that data and passes it on to socket.emit to be sent to the front end, something like this:
Within the object containing the data:
UpdateIfDataIsNew(newData, oldData) {
if (newData !== oldData) { //simplified, not exactly like this
eventBus.emit('dataEvent', newData)
}
}
Where eventBus is a global EventEmitter object.
And in my file handling the sending of the data to the front end, the code is roughly:
ioServer.on("connection", (socket) => {
eventBus.on("dataEvent", (data) => {
socket.emit("frontEndDataEvent", data);
});
})
The way that this is working, whenever a new user connects, a new socket is created, and a new socket.emit is added to the list of functions to be fired when "dataEvent" is triggered, so that all connected users receive the updated data. The problem is that I don't know how to remove a particular socket.emit from the eventBus array of functions when a particular user disconnects, because when I don't do that, all functions stay in the event array, and all of them fire when an event is triggered, althought these users are no longer on the website, eventually the array gets incredibly big, even a simple refresh from a single user, keeps adding more and more functions to the array.
I know that I can see if a user has disconnected with socket.on("disconnect", ???) but then what?
http://prntscr.com/tou4pw the functions are the same, just with a different socket, how do I find the one that must be removed, when someone disconnects?
Instead of just adding a function to the eventEmitter, can I add say an object that contains an ID: and the function, so that I can then quickly identify the one that I must remove?
I solved it by creating my own Event Emitter class (externding EventEmitter), that way I have control over the array of functions for a given event and am also able to assign an ID.
const EventEmitter = require("events");
class RobotData extends EventEmitter {
constructor() {
super();
this.emitters = [];
}
add(socket, dataType) {
this.emitters.push({
socket,
dataType,
trigger: (eventName, newData) => {
socket.emit(`${eventName}`, newData);
},
});
}
remove(ID) {
this.emitters = this.emitters.filter((emitter) => emitter.socket.id !== ID);
}
}
const robotData = new RobotData();
module.exports = { robotData };
And then in the disconnect event:
socket.on("disconnect", () => {
robotData.remove(socket.id);
});

nodejs setTimeout and recursive calls context (this/self)

Im currently working on a project and got stuck at the setTimeout() function in a recursive function. Im rather new to promises so may i did implement this part not corretly too.
The programm should do:
add a listener to a stream event 'readable'
write a request to the stream if specific periodic data is read
resolve promise and remove listener after some other data (answer) is
recieved
Send message from Stream to another process
repeat by calling the same method recursivly with a delay of 10secs
Bassicly im trying to poll from a stream every 10 seconds.
The code looks simplyfied like this:
class XYZ {
myFunction(commands, intervall, i) {
var self = this;
var promise = new Promise((resolve, reject) => {
// I have to write to a Stream and listen for an answer
self.dataStream.write(someData, () => {
self.dataStream.addListener('readable', handleStuff);
});
// Function that handles incoming data from the Stream
var handleStuff = function () {
if (self.dataStream == someFormat) {
self.dataStream.write(commands[i]);
} else {
self.dataStream.removeListener('readable', hadleStuff);
resolve(self.dataStream.read());
}
}
});
// Resolving by sending msg and calling recursivly
promise.then((message) => {
self.send(message);
if (i + 1 > resetValue) {
setTimeout(() => {
self.myFunction(commands, intervall, 0);
}, intervall);
} else {
self.myFunction(commands, intervall, i + 1);
}
});
}
};
And i call it like this:
var myXYZ = new XYZ();
myXYZ.myFunction(myCommands, 10000, 0);
Now when i run this the initial call, it works just fine and sends the message from the dataStream to another process. But when the setTimeout() function is called the function gets "stuck" after writing data to the stream for the first time and the promise is not resolved neither rejected.
My guess is that im mixing up the context (this/self) in my code. Theres sadly no error message, so that i think my logic is faulty. It also works fine if i just remove the setTimeout() function. My Question now is how does setTimeout() change the context from which the code operates?

How to use Promise with redux and socket.io

I am a newbie to react-redux. I am stuck at situation wherein i emit an action to the server. The server in turn makes a third party API request which return a promise. My server.js file for handling socket connection in the server looks like the following
import Server from 'socket.io';
export default function startServer(store) {
const io = new Server().attach(8090);
store.subscribe(
() =>io.emit('curr_news', store.getState().toJS()));
io.on('connection', (socket) => {
socket.emit('curr_news', store.getState().toJS());
socket.on('action', store.dispatch.bind(store));
});
}
As you can see the action is what the client emits and the server makes an appropriate request when it receives the necessary action after which it emits the current state. The following is a sample reducer file
export default function reducer(curr_feeds = CURRENT_FEEDS, action) {
switch (action.type) {
case 'GET_TEST1DATA':
return getTest1data(curr_feeds);
case 'GET_TEST2DATA':
return getTest2data(curr_feeds);
}
}
here getTest1data and getTest2data essentially return a promise as it makes a request to some third part API. My problem is that as the the socket emits the curr_news immediately due to which the value of store.getState() is undefined.
My question is how do I make the store to observe and emit the socket once the promise resolves and not before that? Thanks for the help in advance.
When you subscribe to the store you gain a state whenever it updates. If that new state is a promise, you could process that:
Store.subscribe(state => state.then(promisedData => io.emit(promisedData))

Why is my RxJS Observable completing right away?

I'm a bit new to RxJS and it is kicking my ass, so I hope someone can help!
I'm using RxJS(5) on my express server to handle behaviour where I have to save a bunch of Document objects and then email each of them to their recepients. The code in my documents/create endpoint looks like this:
// Each element in this stream is an array of `Document` model objects: [<Document>, <Document>, <Document>]
const saveDocs$ = Observable.fromPromise(Document.handleCreateBatch(docs, companyId, userId));
const saveThenEmailDocs$ = saveDocs$
.switchMap((docs) => sendInitialEmails$$(docs, user))
.do(x => {
// Here x is the `Document` model object
debugger;
});
// First saves all the docs, and then begins to email them all.
// The reason we want to save them all first is because, if an email fails,
// we can still ensure that the document is saved
saveThenEmailDocs$
.subscribe(
(doc) => {
// This never hits
},
(err) => {},
() => {
// This hits immediately.. Why though?
}
);
The sendInitialEmails$$ function returns an Observable and looks like this:
sendInitialEmails$$ (docs, fromUser) {
return Rx.Observable.create((observer) => {
// Emails each document to their recepients
docs.forEach((doc) => {
mailer.send({...}, (err) => {
if (err) {
observer.error(err);
} else {
observer.next(doc);
}
});
});
// When all the docs have finished sending, complete the
// stream
observer.complete();
});
});
The problem is that when I subscribe to saveThenEmailDocs$, my next handler is never called, and it goes straight to complete. I have no idea why... Inversely if I remove the observer.complete() call from sendInitialEmails$$, the next handler is called every time and the complete handler in subscribe is never called.
Why isn't the expected behaviour of next next complete happening, instead it's one or the other... Am I missing something?
I can only assume that mailer.send is an asynchronous call.
Your observer.complete() is called when all the asynchronous calls have been launched, but before any of them could complete.
In such cases I would either make an stream of observable values from the docs array rather than wrap it like this.
Or, if you would like to wrap it manually into an observable, I suggest you look into the library async and use
async.each(docs, function(doc, callback) {...}, function finalized(err){...})

Resources