Here is something I've learned doing my side project that I think it's fundamental but also often overlooked.
Some context
The project simply contains 2 major parts:
- Front-end: React and Materials UI
- Back-end: REST API using Express, Typescript, Firebase and Socket.io
My goal was to inform the front-end when data is updated using Socket.io so that it would try to fetch data again.
Incorrect attempts
I had these incorrect code snippets that listen to socket event and fetch data again
My first attempt was this, whenever there's any re-render, socket.on('USER_JOINED')
would registers the same callback
function passed into it so once the event USER_JOINED
is emitted, the same callback will fire multiple times while we only need it to execute once to reduce api calls and performance obviously.
const Expense = () => {
const [initialValues, setInitialValues] = useState(null);
const [expense, setExpense] = useState(null);
const { socket, toLogIn } = useContext(SWContext);
// ...
socket.on('USER_JOINED', (socketData) => {
// fetch data again
});
}
My second attempt was to register the callback once when the component is mounted but I still experienced multiple callback executions. It's because even after the component is unmounted, the callback is still registered with socket
(I use one single instance of socket (Singleton pattern)). And I wouldn't have access to new state if the state was updated.
useEffect(() => {
socket.once('USER_JOINED', (socketData) => {
// fetch data again
});
}, []);
Solution
This is what works for me so far. I register a socket event handler and a clean up socket.off
every time expense
changes. This way there's only one socket event handler being called at a time and detached when not needed
useEffect(() => {
socket.once('USER_JOINED', (socketData) => {
// fetch data again
});
return socket.off('USER_JOINED');
}, [expense]);
I imagine this practice can also apply for similar situations like window.addeventlistener()
Take a look at my project if you're curious https://github.com/TueeNguyen/SplitWise3
Top comments (4)
Using
socket.off
like that will unbind all event listeners to that event throughout your app, not just in the component you use it in.It's better to first define the callback for the event listener and bind/unbind that:
This is a great point, this could prevent unbinding all event handlers.
Nice article 👍
Just an FYI, when you’re not doing heavy real-time stuff like multi person chat rooms or something like that you can use Server Sent Events instead a dedicated library like socket.io.
It’s an inbuilt HTML 5 feature and very suitable for lightweight real time apps.
Here’s a little something to get you started - digitalocean.com/community/tutoria...
Thanks, I'll check it out