I have a class where I manage some arrays, they get filled with data that I get from an API I consume. These lists represent clients by state, depending on their status I store these clients in their corresponding list. For privacy reasons I cannot share the entire code with you, but I can extract some things to help you understand my problem.
Let's assume the following where I initialize said arrays like this:
this.waitingClientsList = [];
this.usersAttendedList = [];
this.absentClientsList = [];
this.suspendedClientsList = [];
this.announcedClientsList = [];
this.clientsBatch = [];
Each list will start empty.
To avoid repeating too much code, I manage them with a dictionary like this:
this.listsByState = {
"waiting": this.waitingClientsList,
"suspended": this.suspendedClientsList,
"absent": this.absentClientsList,
"inattention": this.usersAttendedList,
"invideocall": this.usersAttendedList,
"announced": this.announcedClientsList,
"vdconnectingerror": this.usersAttendedList,
"videocallended": this.usersAttendedList,
"vdmissedcall": this.usersAttendedList
}
To update their content I have a function that checks an API method that works with pagination, retrieves the data and stores it in their respective list like this (this is an example):
private GetWaitingClients(state, nextPage, id, pageSize?, pageNumber?) {
this._requestService.getWaitingClients(state, id, pageSize, pageNumber).then(async (response: any[]) => {
// Getting the list by state
let clientList = this.listsByState[state];
// if we are changing the page, we should empty the respective list
if (nextPage) {
clientList = [];
}
response.map(item => {
// If the client doesn't exist, it pushes it to the list
// otherwise I manipulate the client
if (clientList.some(obj => obj.virtualSpaceId == item.virtualSpaceId)) {
// I do something here if the client exists
} else {
// If not, I push it to the list
clientList.push(item);
}
});
[...]
// More stuff that I do
}).catch(error => {
// Here I print the error in case there was a problem with the API request
});
}
I retrieve each list with this line of code let clientList = this.listsByState[state];
, this is the way I found to avoid doing a switch/case statement or multiple if/else statements. So far, this works fine. If there's a new entry in the database, both the local variable clientList
and their referenced array update correspondingly, no problems there.
I have the method in the API built in a way that the first time the page loads it will return the first page with 5 results. If I change the page on the front-end, the function kicks in and should retrieve the next 5 results, so on so forth.
The problem is that when I change the page the variable clientList
does get updated if I change its content but the original referenced array does not, which is bugging me. As far as I understand, when doing this assignment let clientList = this.listsByState[state];
I assume that clientList
is pointing to the same object that its corresponding list in memory but that's not happening here so I don't know where is my problem. I also tried emptying the clientList
variable before doing the map
operation but it does nothing, the original array remains unchanged.
The only way I can manage to change the referenced array content is by doing a direct assignment like this, for example: this.waitingClientsList = clientList
, but this invalidates my previous code and that lefts me with no choice but to do a big and ugly switch/case statement or multiple if/else statements which I don't want to do. I'm trying to keep my code as clean as possible.
Any suggestions? Thanks in advance!
CodePudding user response:
// Getting the list by state
let clientList = this.listsByState[state];
// if we are changing the page, we should empty the respective list
if (nextPage) {
clientList = [];
}
Are you expecting this line of code to also reset the array for this.listsByStatep[state]? It won't do that as you've assigned a new array to clientList and so the reference isn't the same (you can check this by doing console.log(clientList === this.listsByStatep[state])
before and after you assign the []). To do what you're looking for change it to this:
// Getting the list by state
let clientList = this.listsByState[state];
// if we are changing the page, we should empty the respective list
if (nextPage) {
clientList.length = 0;
}
This doesn't destroy the reference and empties the array.
CodePudding user response:
That is a classic JS mistake:
- assign the reference to an object into a new variable (typically to shorten the code)
var short = myObject.some.long.path.or[dynamicKey]
- make a deep change => the original object is correctly modified
short.subProperty = 2; myObject.some.long.path.or[dynamicKey].subProperty === 2;
- re-assign the variable and expect the original object to be modified accordingly => disappointment :-(
short = null; myObject.some.long.path.or[dynamicKey].subProperty === 2;
(In the example I used an object, but it is the same for an array)
In your case, since your test
dynamic key is always in scope, you can simply keep on using it whenever you want to access the corresponding list: this.listsByState[state] = [];
Yes it may look redundant / less readable, but you now have a working code at least.
Now to correctly use your new clientList
variable and keep on modifying the original list, as shown above and implied by MathewBerg's answer, simply make sure to always use a deep property or function that mutates the object in place:
clientList.length = 0; // works
clientList.splice(0, clientList.length); // to empty
clientList.push("itemAtTheEnd"); // works
clientList.unshift("itemAtTheStart"); // works
clientList.splice(2, 0, "insertedItem"); // to insert after the 2nd item
clientList.pop();
clientList.shift();
clientList.splice(3, 1); // to remove the 3rd item
You can easily enforce this rule in your code by preventing re-assignment:
// Initialize a const variable to forbid re-assignment
const clientList = this.listsByState[state];