Pagination in React with fetch() - javascript

I am fetching objects from an API simply with
getData() {
fetch('API_URL').then(res => res.json()).then(data => {
this.setState({ jobs: data.jobs });
}).catch(console.log);
}
componentDidMount() {
this.getData();
}
But I want to be able to click a button to load more objects.
I guess that I should create the API such that it only prints e.g. 10 objects at a time and keeps a variable "pageNumber" and if I click the "load more" button, it should fetch from next page and append the new objects.
Is this the right approach? Or can I simply load thousands of objects when mounting the component and use React to limit how many are seen? I guess this would be a very inefficient way to fetch the data, but I am not really sure how well React handles this.
Can I, for instance, in my API just keep print X number of objects and in my fetch request decide how many objects are loaded? So when I have pressed "load more" 2 times, the API endpoint will return 30 objects instead of only 10 - even though the first 20 have already been fetched before?
I have tried searching for pagination in React, but I only get a lot of pagination libraries. I just want to understand the very basic initial fetching and the fetching following clicking load more.
Edit
But if I have an API endpoint which returns something like
{
page: 1,
objectsPerPage: 10,
numPages: 30,
objects: [
...
]
}
and I am initially retrieving the objects on page 1, and every time I click "Load more", I increase the page number and append the objects on the next page (with this.setState({ jobs: this.state.jobs.concat(data.jobs) }); where data.jobs is the list of objects on the next page, then I would be afraid that new objects are created in the database, so which objects belong to which page is completely screwed up and not all or some duplicates are shown.

Yes, it is the right approach to have a pageNumber on the API, so you only look for the registers you don't have.
On the other size if your data is not too big you can make the fake pagination having all the objects in memory and only showing the ones that you are interested in.
I don't recommend to increase the number of objects you are looking for because you are not getting the advantage of the ones you have already fetched and everytime you increase the number, the request will last more and more.

Related

Unable to access element from array of JSON objects

I am trying to access the specific elements of an array of JSON objects. To test I simply have:
{console.log(this.state.stockCharts)}
This returns (in browser):
This is great, but now I want to access a specific element. Say the first element. I type:
{console.log(this.state.stockCharts[0])}
And the browser is like: nah mate
undefined
It's probably something really simple, but I have been banging my head against my keyboard for the past 45 minutes and nothing has worked. Thanks guys!
Edit 1 (For Akrion)
The query that I am using to access the API is:
https://www.alphavantage.co/query?function=TIME_SERIES_WEEKLY&symbol=MSFT&apikey=demo
This is what I get back from the call:
I call this API twice, and after I get a response back I push it to my stockCharts array:
this.state.stockCharts.push(result)
Edit 2 (For Beginner)
I initialize the state how you would normally do it:
this.state = {
stockCharts: []
}
I verified with the api given in my local and I am able to get the data.
First thing the way you push api response to stockCharts is not recommended. Which means direct mutation of the state is not recommended.
You can push api response in the following way
this.setState(prevState => ({
stockCharts: [...prevState.stockCharts, result]
}));
Now in render
render(){
this.state.stockCharts.map((data, i) => {
console.log("data", data); // this will give each object
console.log("Meta Data", data["Meta Data"]); //This will give meta data information
console.log("Weekly Time Series", data["Weekly Time Series"]);// this will print weekly time information
});
return(
)
}
Is this what your expectation?
It might be because you mutate the state which is not recommended.
try instead of calling this.state.stockCharts.push(result) do this.setState({stockCharts: [...this.state.stockCharts, result]})
https://reactjs.org/docs/state-and-lifecycle.html

How to paginate getting bulk data from firebase with axios?

Assuming I have 1000+ blog posts. What will be the best practice to get data from firebase using axios to store in nuxtServerInit?
Can I somehow get the first 10 blog posts first during the first load and get even more data later on?
Right now I have vuex action as following:
nuxtServerInit(vuexContext, context) {
return axios
.get('https://my-blog.firebaseio.com/posts.json')
.then(res => {
const postsArray = []
for (const key in res.data) {
postsArray.push({ ...res.data[key], uid: key })
}
vuexContext.commit('setPosts', postsArray)
})
.catch(e => context.error(e))
},
You're using the REST API to access the Firebase Database. To retrieve a limited number of items, use a limit query.
https://my-blog.firebaseio.com/posts.json?limitToFirst=10
Since a limit only makes sense when you know how the items are order, you'll want to also order the items, i.e. on their key:
https://my-blog.firebaseio.com/posts.json?orderBy="$key"&limitToFirst=10
Note that the results may not be ordered, since the order of properties in a JSON object is undefined. So the result will contains the first 10 posts, but it may not show them in the right order.
Next step is to get the next 10 items. Unlike on most traditional databases, Firebase doesn't support an offset clause on its queries. So you can't tell it to skip the first 10 items to get to the next 10.
Instead Firebase queries use anchors/ranges: you must know the last item of the previous page to build the query for the next page. Say that the 10th post had a key of keyOfPost10, then you can get the next page with:
https://my-blog.firebaseio.com/posts.json?orderBy="$key"&startAt="keyOfPost10"&limitToFirst=11
We need to retrieve 11 posts here, since we're also getting the 10th post. That also means you'll need to filter the overlapping post in your client code.

unable to view new table data after concat - Ionic 3 Angular 4

I'm using ionic's events to pass data from page to page. In this instance I'm passing an array to another page, let's say with two objects. The data I'm wanting to add to is called dataOne and I'm using a life cycle function so that when the user enters the page they will be automatically tested from this function whether or not there is an event to be concatenated onto dataOne. The issue is, the data isn't being added. I'm able to retrieve the data but nothing happens to the table, as I'm still getting the same result.
ts
ionViewWillEnter(){
this.events.subscribe('market', (dataTwo) => {
return this.dataOne = this.dataOne.concat(dataTwo)
})
}
What is 'Market' ? dataOne is array? In my opinion,
dataOne: any[]=[];
...
this.events.subscribe((dataTwo) =>{
this.dataOne.push(dataTwo.market); // if market you want to catch data
}

jQuery, Bootstrap and big list (7000+ items) with search/typeahead

I know I haven't got any code example, but it's due to the fact that I'm unsure on how to proceed.
I'm building a site with jQuery and Bootstrap, and are going to display a list of around 7000+ items.
I'm using $.getJSON(...) to get the list of items from my PostgreSQL database. This call goes pretty quick.
I would like to create a list which is capable of typeahead search/filter, where elements are displayed corresponding to a user is typing.
I'm not interested in calling my PostgreSQL database more than once - if possible - but would also like to not to kill the browser with DOM elements etc.
What are the best way to proceed, are there any existing components in Bootstrap or...?
ForerunnerDB would be a good library to use: http://forerunnerdb.com/
It's a client side NoSQL db. You'll be able to insert your data into a collection and create a view which can handle auto binding of the data to the DOM. Loading over 7000 DOM elements is a big task, lazy loading is probably the way forward for you; my suggestion would be to display 100 records at a time and when the user is close to the bottom of the list trigger more DOM elements to be rendered:
//Take data from a collection, query it to build a view, and link it to the DOM
db.view('dataView')
.query({
/*
* Filter documents in the collection
* calling db.view('dataView').find() will now only pull records from the 'data' collection which contain a key `verified` that have the value 'true'
*/
verified: true
})
.queryOptions({
//Apply options
//calling db.view('dataView').find() will limit the result to the first hundred records
$limit: 100
})
.from('data')
.link('#targetElement', 'templateName');
To unlink the view again call db.view('dataView').unlink().
When you want to render more records, simply do the following:
var query = db.view('dataView').query();
query.$limit += 100;
db.view('dataView').query(query)
This will automatically render the next hundred records.
Using a combination of .query({}) and .queryOptions({}) will allow you to manipulate the view's data pretty much any way that you want, I've been able to build some complex filtering and searching with ease using this technique.

Ember.js adds elements to collection "magically"

Question related somewhat to: Ember.js: retrieve random element from a collection
I've two routes: randomThing route and things route.
The former displays a... random thing from an API (GET /things/random) (there is a button to "Get another random thing"), the latter: displays all things: (GET /things).
The problem is that EVERY TIME when I click on Get another random thing and new thing is displayed and I go to recipes route this newly displayed random thing is added to the collection...
Action to get random thing performs a find("random") as suggested in related question and sets this.content to this value.
What is wrong here?
EDIT:
I'm using ember-data and my route is like this:
App.ThingsRoute = Ember.Route.extend({
model: function() {
return App.Thing.find();
}
});
The problem is that EVERY TIME when I click on Get another random thing and new thing is displayed and I go to recipes route this newly displayed random thing is added to the collection...
This is expected behavior. App.Thing.find() does not simply query the api and return results. Instead find() returns an array containing of all Things ember knows about. It includes objects returned by past calls to find(), objects created client-side via App.Thing.createRecord(), and of course individual objects queried via App.Thing.find('random'). After returning this array, find() and kicks off another API call and if that returns additional records they are pushed onto the array.
What is wrong here?
It does not sound like anything is wrong per-se. If you want to prevent random things from showing up in the ThingsRoute, you'll need to change that route's model to be a filter instead of just returning every Thing. For example:
App.ThingsRoute = Ember.Route.extend({
model: function() {
//Kick off query to fetch records from the server (async)
App.Thing.find();
//Return only non-random posts by applying a client-side filter to the posts array
return App.Thing.filter(function(hash) {
if (!hash.get('name').match(/random/)) { return true; }
});
}
});
See this jsbin for a working example
To learn more about filters I recommend reading the ember-data store-model-filter integration test

Categories