Search code examples
javascriptnode.jsarraystypescriptapply

Array change listener callback not getting arguments


So I've been scratching this mental itch and was checking out how to handle change listeners to arrays without using Observables or external libraries.

The main idea is that I have a .json file which is basically an array of objects (of type Rent) and I'll use that file as my DB. I want to trigger a write to the file whenever the rentCollection array is changed.

With the help of search engines and SO, I landed on this answer, and I've managed to implement something similar that seems to work as intended with a notable exception:

  • The callback function doesn't receive any arguments, even if they're being passed and I'm a bit at a loss as to why so I'm looking for you help.

Given the following piece of code

const rentsFile = __dirname + "/data/rents.json";

export let rentCollection: Rent[] = [];

const populateRents = () => {
  let rents = fs.readFileSync(rentsFile, { encoding: "utf8" });
  rentCollection = JSON.parse(rents);
}

export const initDb = () => {
  populateRents();

  listenChangesinArray(rentCollection, writeFile);
}

/* @arr array you want to listen to
   @callback function that will be called on any change inside array
 */
const listenChangesinArray = (arr: any, callback: any) => {
  // Add more methods here if you want to listen to them
  ['pop', 'push', 'reverse', 'shift', 'unshift', 'splice', 'sort'].forEach((m: any) => {
    arr[m] = (arg: any) => {
      var res = Array.prototype[m].apply(arr, arg);  // call normal behaviour
      callback.apply(arr, arg);  // finally call the callback supplied
      return res;
    }
  });
}

const writeFile = (arg: any) => {
  console.log("Write Arg: " + arg); // <== This argument is always undefined. Why???
  try {
    console.log(JSON.stringify(rentCollection, null, 2)); // <== I never see the new item in this collection. Why???
    fs.writeFileSync(bloqsFile, JSON.stringify(rentCollection, null, 2), 'utf8');
    console.log('Data successfully saved to disk');
  } catch (error) {
    console.log('An error has occurred ', error);
  }
}

whenever I do a rentCollection.push(Rent), it will trigger the listenChangesinArray method, and I can see/log the arg as being the passed Rent object, and then call the writeFile method too. However, when getting to writeFile the argument is always undefined. Why?? Is the way to handle the changes listener not right?

Appreciate any insight you can give. Thanks!


Solution

  • The second argument of apply should be an array, but you pass it the argument that you provided to the array method, such as to push, if any.

    Fix this by getting all arguments in an array. So change this:

    arr[m] = (arg: any) => {
    

    to:

    arr[m] = (...arg: any) => {
    

    Now arg will be an array, and so the second argument to both calls of apply will have the correct type.