skip to Main Content

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!

2

Answers


  1. 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.

    Login or Signup to reply.
  2. The answer given by trincot is pretty much valid and works well…

    But I would like to add some hints to you.

    1. Use ...args instead of ...arg to make it clear that args is a list/array.
    2. If that was the only thing you did, note that your writeFile function should also be changed to ...args and properly adjusted (it will be receiving ...args, but only reads the first?).
    3. Avoid making your listenChangesinArray override sort and reverse methods and any other like those. Instead, execute them before or after any new write to the array. Else, if you sort then add a new item (or do the other way around), the array will be saved twice in a row… Depending on your service this can avoid some extra charges for resource consumption and save your storage lifespan…

    PS.: Most storage systems have a lifespan measured in cycles of writings. If you’re doing double writes, then your storage lifespan will be cut in half.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search