save results from one api in different arrays - javascript

I have an action in pinia which I do a get to an api where I get some elements, the api returns the pagination with the current page, elements per page, total pages and total elements, the get only returns 20 elements for each Every time I call the action, when I load a component I call it and it returns the data (20 elements) and I save it in an array to then go through it and show it in cards, I have a button where I call the get action with the page in +1 to bring the other 20 elements. I am trying to get all the elements when loading my component, the way to get them all would be by clicking on the button which would return 20 by 20, but how can I make it so that when loading the component it calls all the elements without affect the array that goes from 20 to 20 and save them in a different array? In an array I save 20,40,60..., but in the other I would save the total elements.
>action
fetchElements(query = "", params) {
return new Promise((resolve) => {
this.loading = true;
const index =
stage_amp === undefined || stage_amp === "dev"
? client.initIndex("req-dev")
: client.initIndex("req-prod");
index
.search(query, params)
.then(({ hits, page, hitsPerPage, nbPages, nbHits }) => {
hits.forEach((hit) => {
this.elements.push(hit);
});
this.pagination = {
page: page,
perPage: hitsPerPage,
totalPages: nbPages,
totalHits: nbHits,
};;
});
});
},
>I get the first 20 elements
getElements.fetchElements();
>button that brings me the other 20 elements
const pagination = () => {
getElements.fetchElements(query, { page: getElements.pagination.page + 1});
}
pensaba

As far as I can understand, you want a way to get the 20 elements from the current page, but you still want to fetch all the elements ?
There several ways to this, but the simplest (in my opinion) would be to have an array of all your pages, and an index to keep track of your current page.
For example, the structure may look like this :
// In your store
const state = () => {
elementsPages: [
[...], // elements from page 1
[...], // elements from page 2
[...] // elements from page 3
],
currentPage: 0
}
That way, you could have 2 differents computed, one to get only the 20 elements from your current page, and one to get all the elements
// in your component
export default {
computed: {
currentPageElements() {
const allPages = this.$store.state.elementsPages
const currentPage = this.$store.state.currentPage
return allPages[currentPage]
},
allElements () {
return this.$store.state.elementsPages.flat()
}
}
}
This works assuming that elementsPages is always an array, and currentPage is an Int between 0 and elementsPages.length. The code is just a simplified sample that you may need to adjust to fit your needs.
This is, in my opinion, a good way to manage your store state since you will not duplicate your data (meaning that you don't have a property that contains the first 20 elements, AND another property that contains everything)
Also, note that the elementsPages property is an array of arrays. Instead of :
hits.forEach((hit) => { state.elements.push(hit) })
You may need to use something like this :
state.elementsPages.push(hits)
Hope that helps ;)

Related

Js/React : Find ONLY the elements whitin an array that matches X condition to get X results

Im getting different elements from an array that comes from the backend like so :
data = [{id: '1', collection: 32, isNew: true},{id: '5', collection: 22, isNew: false}, .... ]
The user is allowed to select in a massive way differents Ids, and edit them, and I need to show a different modal for each different cases :
The conditions to show the differents modals are based on : If I select only the ones that has isNew = true, if I select only the ones with isNew= false, or if I select both Id cases isNew=false and isNew=true.
So far Ive tried this :
const getNewInfo = data.some(item => item.isNew)
if(getNewInfo) {
return this.explainNewInfo(true)
} else if(!getNewInfo) {
return this.explainNewInfo(false)
} else {
return this.explainNewInfo()
}
I also tried with the filter method and push to a new array, but it relies on the same logic at the end. For the first both cases, it works fine, but in the 3rd case, I cant get in, since whenever matches that isNew is true, it gets in there, and discard all the rest of posibilites.
Each function that is being called on the if else, is a function that recives or not a parameter indicating if is necessary to show a certain modal for the 3 differentes cases.
You can use every function on an array to check for all objects. Read about every.
const isAllNewInfo = data.every(item => item.isNew)
const isAllOldInfo = data.every(item => !item.isNew)
if (isAllNewInfo) {
return this.explainNewInfo(true)
} else if(isAllOldInfo) {
return this.explainNewInfo(false)
} else {
return this.explainNewInfo()
}

Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. - React

So I'm trying to make a screen where data from user's localstorage is used (Lets call it var1) but I'm getting Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. error. What I'm trying to do is check if the data from user's localstorage exists and if it does then it will put that data into a state but first it will grab another variable from localstorage (User auth token, lets call it var2) and put it into every object in var1 (var1 is a list which contains objects) and this is done using map then the state is set to the changed var1 with the auth token(or var2), then it returns some HTML and some logic is used in HTML, For every object in var1 it will create a new select tag with numbers ranging from 1 to 20 and this is done using mapping an array with 20 numbers (I'm doing this because I could not get for loop to work properly) and if the current number of option in select tag matches a key value pair in one of var1's object then it will
select the option tag or put selected attribute on option tag and if you change the value of select tag then it will trigger a function which will map through var1 and select the object which user requested and change the value of quantity to whatever the user selected on select tag. I tried to cut down and simplify my code as much as I could. My code is like this:
function RandomScreen() {
const [var1, setvar1] = useState([])
let localstoragevar = JSON.parse(localStorage.getItem('var'))
let newCart = []
if (localstoragevar) {
localstoragevar.map(item => {
item.authtoken = localStorage.getItem('AuthToken')
newCart.push(item)
})
}
setvar1(newCart)
let twenty = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
return (
{var1.map(newItem => {
{/* HTML code goes here */}
{twenty.map(number => {
if (number == item.quantity) {
return (
<option onChange={handleClick} selected name={newItem.id} value={newItem.quantity}>{newItem.quantity}</option>
)
} else {
return (
<option onChange={handleClick} name={newItem.id} value={number}>{number}</option>
)
}
})}
})}
)
}
Your render calls setvar1 which in turn trigger's a re-render.
You should put this whole logic inside a useEffect hook:
useEffect(() => {
let localstoragevar = JSON.parse(localStorage.getItem('var'))
let newCart = []
if (localstoragevar) {
localstoragevar.map(item => {
item.authtoken = localStorage.getItem('AuthToken')
newCart.push(item)
})
}
setvar1(newCart)
}, []);
This is what you have to do is to avoid logic in your render function. For that case, we do have useEffect function plus on top of that you may add useMemo;

Pass value function

I have an application that populates the array continuously until it is stopped and it does two things:
If you click Stop button, it writes values in the DB.
Every 1000sec it checks the size of array and if it is > 2000 write the values in the db.
Now I have a problem:
I use the first element of the array to do some calculations, before writing the data to the db.
So if the array exceeds the size of 2000, it performs a splice and passes the array to another page, taking the first element as the basis for the calculation that will be performed on the next page.
At this point, if the user clicks the stop key as the basis for the operations, the last element of the array previously passed must be used.
For example:
array = [0, 20, 40, ......, 2000,..]
array.length > 2000
arrayBase = 0 // I use it for operations.
// Do a splice
array = [2020, 2040, ...... ]
array.length < 2000
//User click stop Button
//I should pass as arrayBase the last value of array (2000)
I hope at least I have explained myself by example.
This is my code:
//this function populate array until I click stop
populateArray(){
this.arrayTimestamp.push(`${buf.readInt16LE(0)}`);
this.firstElementTimestamp = this.arrayTimestamp[0];
//.....
}
//This function check the size and write in the DB if > 2000
checkSize(){
that.timeout = setInterval(function() {
if( (that.arrayTimestamp.length > 2000 ){
that.arrayTimestampCopy = that.arrayTimestamp.splice( 0, 2000 );
//this is a function in other page where I do some operations
scrittura.write({
counterTimestamp: that.firstElementTimestamp,
//...
})
.then(response => {
//...
})
// I have tried something like this:
that.firstElementTimestamp = that.arrayTimestamp[2000] //obviously it is undefined as the array with the splice has been emptied
}, 1000);
}
//this is the function when the Stop button is clicked.
stopConnection(){
Actions.Activity({
counterTimestamp: this.firstElementTimestamp,
//...
})
}
So my goal is to find a way to always use the same base in the calculations, without it being updated.
How can I do?
I think you should use array reduce or Promise All (based on which one you need, parallel or not)
arr.reduce((prom, item) => {
return prom.then(() => {
return scrittura.write(item).then((result) => ... );
});
}, Promise.resolve()).then(function() {
// all done here
}).catch(function(err) {
// error here
});
or use Promise All for parallel
You can see another example here
Synchronous loop in Promise all

SAPUI5 Javascript - Get first and last elements of array for each unique property

SAPUI5 - I have an array of objects and one of the properties in those is 'Category'.
For example say I have 2 different types of Category, 'Front Shop' and 'Production Area', what I need to do is to be able to get the first value of each and the last value of each, and then set the enabled property of a button as enabled/disabled.
I'm currently using undercore js (_.each) to loop through to perform some other logic, so can include additional logic here.
Not sure if Underscore has a built in function for this?
Or could someone point me in the right direction on how to do this?
I've got my first pass at what was wanted where I get the very first result and the last result, but now need to set this for each unique category.
Example code below:
// Set view data
oViewData.Questions = oQuestions.results;
oViewData.Questions.TotalNumberOfQuestions = oQuestions.results.length;
// Loop Questions, to get Category Desc and Competency Desc values from relevant Sets
_.each(oViewData.Questions, function (result, index) {
// Read and set Category Desc
this.getView().getModel("Survey").read("/CategorySet", {
filters: [new Filter("CategoryId", FilterOperator.EQ, result.CategoryId)],
success: function (oData) {
oViewData.Questions[index]._CategoryDesc = oData.results[0].CategoryDesc;
this.setViewData(oViewData);
}.bind(this),
error: function (oError) {}.bind(this)
});
// Read and set Competency Desc
this.getView().getModel("Survey").read("/CompetencySet", {
filters: [new Filter("CompetencyId", FilterOperator.EQ, result.CompetencyId)],
success: function (oData) {
oViewData.Questions[index]._CompetencyDesc = oData.results[0].CompetencyDesc;
this.setViewData(oViewData);
}.bind(this),
error: function (oError) {}.bind(this)
});
// Set all move up / down buttons to enabled
oViewData.Questions[index]._MoveUpBtn = true;
oViewData.Questions[index]._MoveDownBtn = true;
// if category id is the first one in the list
}.bind(this));
// Overwrite first move up button and last move down btn to disabled
oViewData.Questions[0]._MoveUpBtn = false;
oViewData.Questions.slice(-1)[0]._MoveDownBtn = false;
// Set view data
this.setViewData(oViewData);
First, you can iterate through arrays with native JavaScript.
_.each(array, function(item) {}) is the same as array.forEach(function(item) {}).
Second, you can use the built-in filter function for your actual question:
const aFrontShopItems = oViewData.Questions.filter(function(oItem) {
return oItem.Category === "Front Shop";
}
If oViewData.Questions is an array then the function passed to filter is applied to every element. If the condition (e.g. oItem.Category === "Front Shop") is true then the element is added to the new array aFrontShopItems. Obviously you need to call filter a second time to get the Production Area items. You can then apply your logic to the first and last items of your new arrays.

Implementing Infinite Scrolling with Firebase?

var self = this;
var firebaseRef = new Firebase(baseUrl + '/sparks');
firebaseRef.limitToLast(5).on('child_added', function(childSnapshot, prevChildKey) {
self.addChild(childSnapshot); // adds post to a <div>
});
My code currently loads the last 5 posts and will load any new posts. However, I'd also like to be able to load older posts as well. I have a button that when clicked will call a function (that I'm unsure of how to implement) that loads older posts. How do I retrieve these older posts?
(The arrow just signifies that I want to retrieve posts starting from the bottom and working my way up to the top)
You need to think a bit backwards to do this. When you get the results for your query for the first page, remember the first item in the results:
firebaseRef.endAt().limitToLast(5).on('child_added', function(childSnapshot, prevChildKey) {
self.addChild(childSnapshot); // adds post to a <div>
});
While you cannot access child items by index with Firebase, you can store the key of an item and use that to start a next query.
var firstKnownKey;
firebaseRef.orderByKey().limitToLast(5).on('child_added', function(childSnapshot, prevChildKey) {
if (!firstKnownKey) {
firstKnownKey = childSnapshot.key;
}
self.addChild(childSnapshot); // adds post to a <div>
});
Now you have a variable firstKnownKey that has the first key you've ever seen. To get the previous batch of children, you pass that value in to endAt() when you fire your next query:
firebaseRef.orderByKey().endAt(firstKnownKey).limitToLast(5).on('child_added', function(childSnapshot, prevChildKey) {
if (!firstKnownKey) {
firstKnownKey = childSnapshot.key;
}
self.addChild(childSnapshot); // adds post to a <div>
});
Answers to similar questions of the past few days:
Can I get the nth item of a firebase "query"?
Firebase results range using startAt and endAt
Since endAt() is inclusive, the last item gets repeated every time I do the infinite scroll, so I did a little modification to frank van puffen 's answer.
I initiate a list childrenVal to store all the values, another list childrenKey to store all the keys and a var firstKnownKey, as frank van puffen sugests.
var childrenVal=[];
var childrenKey=[];
var firstKnownKey = "";
For the first time you make the query, you get the last 5 elements:
getFirst(){
firebaseRef.orderByKey().limitToLast(5).once('value')
.then((snap)=>{
snap.forEach(childSnap => {
childrenVal.unshift(childSnap.val());
childrenKey.unshift(childSnap.key);
});
firstKnownKey = childrenKey[childrenKey.length-1];
});
}
In your next query, you won't want your firstKnownKey to get repeated, so I did the following function:
exclude(key){
return key.substring(0, key.length - 1) + String.fromCharCode(key.charCodeAt(key.length - 1) - 1)
}
and for the query itself, the following function:
getNext() {
firebaseRef.orderByKey().endAt(exclude(firstKnownKey)).limitToLast(5).once('value')
.then((snap) => {
snap.forEach(childSnap => {
childrenVal.unshift(childSnap.val());
childrenKey.unshift(childSnap.key);
});
firstKnownKey = childrenKey[childrenKey.length - 1];
});
}

Categories

Resources