Generating non duplicate items in componentDidMount using Redux - reactjs

I am creating a multipage application. When the user navigates to one of my pages componentDidMount triggers and currently generates 4 random numbers within a range I have specified and sends them through my reducer. This eventually comes back in the form of an array.
Within componentDidMount I have a loop to call my generateRandomNumber function four times. However, if I go to log the output array I get a blank array, which is the initial state for said reducer.
Logging the array in componentDidUpdate, or anywhere after the mount returns what I want it to but that doesn't help me in checking for duplicates as I cannot check for duplicates if I am checking against a blank array. Everything should be without delay, but there seems to be an issue with trying to read from this.props.currentUserList from within componentDidMount.
I need to verify that I am not adding two of the same user into my array, but I need the pre-check to all be done by the time this appears on-screen.
//gives me a random user from my array
generateRandomUser() {
return myArray[Math.floor(Math.random() * 100) + 1]
}
//sends 4 randomUsers through my actions to my reducers
generateNewID() {
let amountOfOptions = 4;
while (amountOfOptions > 0) {
let randomUser = this.generateRandomUser();
if (this.props.currentUserList.length === 0) {
this.props.setCurrentUser(randomUser);
} else {
//this never fires as currentUserList is somehow still == []
this.checkForDuplicates();
}
amountOfOptions--;
}
}
componentDidMount() {
this.generateNewID()
console.log(this.props.currentUserList)
//returns []
}
componentDidUpdate() {
console.log(this.props.currentUserList)
// returns [
// user2: {
// name: 'name'
// },
// user4: {
// name: 'name'
// },
// user28: {
// name: 'name'
// },
// user92: {
// name: 'name'
// },
// ]
}

I am not sure about the implementation of the array. However if the required output is to get 4 random users then instead of of calling setcurrentuser 4 times, you can generate 4 users and then set them at once.
generateNewID() {
let amountOfOptions = 4;
let randomUserList = [];
while (amountOfOptions > 0) {
let randomUser = this.generateRandomUser();
if (randomUserList.length === 0) {
randomUserList.push(randomUser);
} else {
//this never fires as currentUserList is somehow still == []
this.checkForDuplicates();
}
amountOfOptions--;
}
this.setCurrentUser(randomUserList);
}

Related

Update a specific value of an object which is inside an array

So im building an app mock up , upon sending a message I need to notify all the participants of the chat that they have an unread message, My participant type has a field called "unread" which is an array of objects like such
unread:[
{chatId:'13213', msgs:3},
{chatId:'132546', msgs:1}
];
chat id references to a unique conversation, and msgs number shows new messages sent , the length of msgs array is the number of unread msgs you have , what I want to do is , when a new msg is sent I want to loop through the unread array and
condition one Array is completely empty :
add an object to it
condition two array already has objects, then find the one that has the same chatId as the one I will pass in the function and increase the message count
condition three, array already has objects but not with the chatId I provided, in which case create a new object and add it to an existing array.
any ideas on how I can go about this?
I tried doing the following but it doesn't work
emp.unread.length < 1
? [{ chatId: chatId, unread: 1 }]
: emp.unread.map((cht) => {
if (cht.chatId === chatId) {
return {
...emp.unread,
chatId: chatId,
unread: cht.unread + 1,
};
} else {
return { ...emp.unread, chatId: chatId, unread: 1 };
}
}),
You can try using the if condition as below:
You can use filter/find to complete you tasks normally.There could still be other less resource consuming way but this way you will get your task done.
if(unread.length == 0 ){
//pushing the object
}else{
if(chechExistingChat(chatid)){
addOnExistingChat(chatId)
}
else{
//unread.push(new object)
}
}
function checkExistingChat(chatId){
let sameChatArray = unread.filter((item)=>{
return item.chatId == chatId
})
return !!sameChatArray.length
}
function addOnExistingChat(chatId){
let finalArray = unread.map((item)=>{
if(item.chatId == chatId){
return {...item,msgs:item.msgs+1}
}
return item
})
return finalArray
}

Arraylist doesn't get refilled and/or filtered

I have a list in angular, an array. OnInit it gets filled from the right corresponding database items. I created a form above it. When you enter something in the form, it acts like a filter. This works, the first time. When you erase something from the form and enter something else, the list should be refreshed and afterwards filtered based on the new input. This doesn't happen. I put the formula that happens on onInit in my function to refill the list.
Below you can find my function (I left the console logs in) and a screenshot of the problem. First I look for a user (joeri.boons#hi10.be) which returns three results. Than I erase the user and look based on a month 7. The screen returns a new unfilterd list while in the console it still holds the list of 3 x user joeri.boons#hi10.be. So there is an inconsistency to. If you look at screen result you would think of a filter problem, the console points at a refreshproblem.
if more code is required let me know.
updateWithFilter(): void {
console.log("function update filter reached")
console.log(this.listadapted);
if(this.listadapted == true){
// this.timesheetsHandled = {} as TimeSheet[];
this.getHandledSheet();
console.log("getHandledSheet executed")
}
if(this.filterUsername.trim() && !this.filterYear && !this.filterMonth){
console.log("option 1 reached")
console.log(this.filterUsername.trim());
console.log(this.filterYear);
console.log(this.filterMonth);
this.timesheetsHandled = this.timesheetsHandled.filter(sheet => sheet.username == this.filterUsername);
this.listadapted = true;
} else if(!this.filterUsername.trim() && !this.filterYear && this.filterMonth){
console.log("option 2 reached");
console.log(this.filterUsername.trim());
console.log(this.filterYear);
console.log(this.filterMonth);
console.log("before filter");
this.timesheetsHandled.forEach(sheet => console.log(sheet.username));
this.timesheetsHandled = this.timesheetsHandled.filter(sheet => sheet.month == this.filterMonth);
console.log("after filter");
this.timesheetsHandled.forEach(sheet => console.log(sheet.username));
// console.log(this.timesheetsHandled.filter(sheet => sheet.month == this.filterMonth));
this.listadapted = true;
} else if .. more options
}
ngOnInit(): void {
this.getHandledSheet();
}
getHandledSheet(): void {
this.timesheetService.getAllTimesheets().subscribe({next: (response: TimeSheet[]) => {this.timesheetsHandled = response.filter(sheet => sheet.status == 'HANDLED') }}) ;
}
My guess would be that this is caused by loading data in ngOnInit. As the documentation (https://angular.io/api/core/OnInit) states : [...] It is invoked only once when the directive is instantiated.
I suspect that you create one instance and re-use it and the ngOnInit method does not get called again.
UPDATE:
The issue is that the call to this.getHandledSheet(); does a call to .. .subscribe({next: .. which is delayed and the rest of the function is executed first.
So the actual code after next: is only executed after the timeSheetService is done loading the data.
So either you apply the filter in the
{next: (response: TimeSheet[]) => {this.timesheetsHandled = response.filter(sheet => sheet.status == 'HANDLED') }}
block after filtering for 'HANDLED' or you'll try to await in the update function.
Create two variables, one that will always remain unfiltered, then another that will be filtered.
The problem will be that the original list is filtered, hence you are losing the original data after filtering!
timesheetHandled: TimeSheet[];
timesheetHandledOriginal: TimeSheet[];
updateWithFilter(): void {
console.log('function update filter reached');
console.log(this.listadapted);
if (this.listadapted == true) {
// this.timesheetsHandled = {} as TimeSheet[];
this.getHandledSheet();
console.log('getHandledSheet executed');
}
if (this.filterUsername.trim() && !this.filterYear && !this.filterMonth) {
console.log('option 1 reached');
console.log(this.filterUsername.trim());
console.log(this.filterYear);
console.log(this.filterMonth);
this.timesheetsHandled = this.timesheetHandledOriginal.filter(
sheet => sheet.username == this.filterUsername
);
this.listadapted = true;
} else if (!this.filterUsername.trim() && !this.filterYear && this.filterMonth) {
console.log('option 2 reached');
console.log(this.filterUsername.trim());
console.log(this.filterYear);
console.log(this.filterMonth);
console.log('before filter');
this.timesheetsHandled.forEach(sheet => console.log(sheet.username));
this.timesheetsHandled = this.timesheetHandledOriginal.filter(
sheet => sheet.month == this.filterMonth
);
console.log('after filter');
this.timesheetsHandled.forEach(sheet => console.log(sheet.username));
// console.log(this.timesheetsHandled.filter(sheet => sheet.month == this.filterMonth));
this.listadapted = true;
}
// else if .. more options
}
ngOnInit(): void {
this.getHandledSheet();
}
getHandledSheet(): void {
this.timesheetService.getAllTimesheets().subscribe({
next: (response: TimeSheet[]) => {
this.timesheetsHandled = response.filter(sheet => sheet.status == 'HANDLED');
this.timesheetHandledOriginal = JSON.parse(JSON.stringify(this.timesheetsHandled));
},
});
}

Add callback to function that is called from inside arr map method

I need to map over an array in my state and assign the returned value to another property in my state.
I then need to call a function using the value of the updated property as a parameter, wait for this function to complete and then move on to the next item in my array map method before repeating the process.
The problem is that I think my function is being run before the state has been updated through each iteration of my arr.map method.
I think I need to utilise the componentDidUpdate() method before running my function, but I am not sure how to implement that in this scenario.
Simpler to explain through code example, see below (edited for simplicity):
state = {
a: [ {name: 'some string'}, {name: 'some string'}..... ],
b: '' // empty string
}
// when button is clicked, this function is run
myFunc() {
this.state.a.map( (item) => {
this.setState({
b: item.name
})
mySecondFunc()// perform next part of the loop
})
}
mySecondFunc() {
alert( this.state.b )
}
The alert in mySecondFunc() does not return anything, it is not being updated before the function is run.
What I need to happen is that the map will get the first item from my states 'a' array, assign it to state 'b' and run mySecondFunc().
I need to wait for state to be updated, before calling mySecondFunc, and then wait for mySecondFunc() to end its function before the map gets the next item from my state's 'a' array and calls mySecondFunc again.
This is the part that I cannot figure out.
try this
state = {
a: [ {name: 'some string'}, {name: 'some string'}..... ],
b: '' // empty string
}
// when button is clicked, this function is run
myFunc(){
this.state.a.map(async(item) => {
await this.setState({
b: item.name
},async () => {
await mySecondFunc()// perform next part of the loop
})
})
}
mySecondFunc() {
alert( this.state.b )
}
//OR
myFunc(){
this.state.a.map(async(item) => {
await mySecondFunc(item)
})
}
mySecondFunc(item) {
alert( item )
}
What I have understood from the problem description is that the goal here is to pass b's value sequentially to mySecondFunc . According to my understanding of the problem, I have modified your code :
myFunc() {
this.state.a.map( (item) => {
this.setState( (prevState) => {
const modifiedName = item.name;
this.mySecondFunc(modifiedName);
return {
b: modifiedName
}
});
});
}
mySecondFunc(name) {
alert( name );
}

How to Iterate Over AJAX Call and Push to Array

In this code I:
Get a list of items (via AJAX)
Get more items details (via 2nd AJAX call)
Push item + details to history array
Code:
<template is="dom-repeat" items="[[history]]" index-as="index">
<p>[[item.itemId]]>
<p>[[item.priceNet]]</p>
</template>
...
getRequest() {
let request = this.$.ajax.generateRequest();
request.completes.then(req => {
// returns this.data;
this.i = 0;
console.log(this.history); // Logs several objects in array on 1st iteration
this.getRequest2();
})
}
getRequest2() {
if (this.i<this.data.length) {
this.itemId = this.data[this.i].itemId;
let request = this.$.ajax2.generateRequest();
request.completes.then(req => {
this.updateHistory();
})
}
}
updateHistory() {
if(typeof this.responseStock.error == 'undefined') {
this.push('history', {
"itemId": this.response[0][this.i].itemId,
"priceNet": this.responseStock.priceNet
})
} else {
this.push('history', {
"itemId": this.response[0][this.i].itemId,
"priceNet": null
})
}
this.i = this.i + 1;
this.getRequest2();
}
However, upon the first instance history logs multiple objects in the array, so the push method doesn't properly update.
How can I pass the data through so that each item that gets returned from the 1st AJAX call gets attributed with the 2nd AJAX call if attributes exist, and then push it to an array?
Note: the this.push is from the Polymer framework, but the problem seems to be that the timing of the push method is off...

Firebase - How to update many children and not delete others in AngularFire

I want to use update() on firebase ref to update many children in one operation.
To do this I passed the object with values to change.
Here is the output of console.log(angular.toJson(change,1))
{
"10": {
"otherRubies": 30
},
"11": {
"otherRubies": 30
}
}
At the beginning i have:
Then i do:
var refUsers = new Firebase(FBURL).child('users/');
refUsers.update(change);
So i want to have:
but instead of that i get:
Is there any way to do that ?
Update is not a recursive operation. So it's calling set on the child paths. If you call update at users/, you're saying don't delete any keys under user/ which are not in my data, when you want to say, don't delete any keys in the child records of user/.
Instead, iterate the records and call update on each:
var refUsers = new Firebase(FBURL).child('users/');
for(key in change) {
if( change.hasOwnProperty(key) ) {
refUsers.child(key).update( change[key] );
}
}
With Firebase 3 you can do the update by writing :
update = {};
update['10/otherRubies'] = 30;
update['11/otherRubies'] = 30;
refUsers.update(change);
This way of updating the data is not in the documentation but it worked for me.
This is a sample of recursive update:
function updateRecursively(path, value) {
for (let key in value) {
if (value[key] instanceof Object) {
updateRecursively(`${path}/${key}`, value[key]);
} else {
firebase.database().ref(`${path}/${key}`).set(value[key]);
}
}
}
Usage:
updateRecursively('users', {
"10": {
"otherRubies": 30
},
"11": {
"otherRubies": 30
}
})

Resources