Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node .on method firing too many times

I have an Electron app that presents a directory listing to the user. When the user clicks a button my interface script, interface.js, clears the container div and sends a message to main.js. On receiving the message, main.js scans the directory into an array of filenames and returns the array to interface.js as a response. Interface.js uses a .on method that fires when the response is received and updates the container div with the contents of the array.

This is my first real attempt at using Node, and as far as interface behaviour went everything worked brilliantly! Wonderful, it's only been a few hours and I'm already loving Node!

However, while debugging/stress testing I printed the returned array within the .on method to the console and noticed some strange behaviour. The first time the user clicks the button, the .on method runs once (verified by one message to the console). The second time the user clicks, the method runs twice (verified by two messages to the console); the third time it runs three times and so on.

The function in main.js that scans the directory only runs once per click, so the issue must be within inteface.js.

My code for main.js and interface.js:

main.js:

const {app, BrowserWindow, ipcMain} = require('electron');
const fs = require('fs');

...

ipcMain.on( 'list-directory', ( event, directory ) => {
    var files = fs.readdirSync( directory );
    event.sender.send( 'list-directory-reply', files );
});

interface.js

var { ipcRenderer, remote } = require( 'electron' );  
var main = remote.require( "./main.js" );

...

button.addEventListener('click', function(){ showDialogue( this ); }, false );

...

showDialogue( select ) {
    // clear the dialogue
    // some other stuff
    ipcRenderer.send( 'list-directory', './files/documents/' );
    ipcRenderer.on( 'list-directory-reply', function( event, contents ) {
        console.log( contents );
        if ( contents.length > 0 ) {
            // add contents to the dialogue
        }
    } );
}

The code is adapted from a tutorial on the Electron website.

Why does ipcRenderer.on run multiple times? Is it possible that it's bound to something every time the button is clicked, and thus runs as many times as past clicks? I put a print statement inside the event listener function, and inside the showDialogue function before the ipcRenderer stuff, but they both only printed once per click so the repeats are definitely only coming from ipcRenderer.on.

like image 842
jla Avatar asked Aug 31 '18 08:08

jla


2 Answers

You are subscribing to ipcRenderer.on after every button click which is causing multiple subscriptions. Try to define the ipcRenderer.on event handler outside click event and it should work fine.

Something like this -

button.addEventListener('click', function(){ showDialogue( this ); }, false );


ipcRenderer.on( 'list-directory-reply', function( event, contents ) {
    // ipcRenderer event handler
});

showDialogue(select) {
    ipcRenderer.send( 'list-directory', './files/documents/' );
}
like image 65
planet_hunter Avatar answered Oct 16 '22 15:10

planet_hunter


As @planet_hunter mentioned, you are setting up a listener every time showDialogue() is called. You need to remove the listener or move the listener outside of the calling function.

However, I find a cleaner solution is to use the .once command. This behaves like .on, but instead of manually having to remove the .on listener (which you haven't done) it removes itself.

showDialogue( select ) {
    // clear the dialogue
    // some other stuff
    ipcRenderer.send( 'list-directory', './files/documents/' );
    ipcRenderer.once( 'list-directory-reply', function( event, contents ) {
        console.log( contents );
        if ( contents.length > 0 ) {
            // add contents to the dialogue
        }
    } );
}
like image 5
psiphi75 Avatar answered Oct 16 '22 16:10

psiphi75