I want to show some news in my React app using Redux.
The problem is that I want to show the news for individual dates and I want to paginate the news.
In my API I print
{
pagination: {
count: 1000,
size: 10,
page: 1,
pages: 100
},
news: [
..
]
}
I know how to make a simple pagination, but I don't know how the API should work if I want to be able to show news for different dates in my app.
Until now (without dates), I have just kept a state news
and pagination
in my Redux reducer, and then checked if the page number equals the total number of pages to determine whether it should try loading more news.
But now that I potentially have many different dates and I want to keep all the news in the Redux store, I don't know how to structure it.
I can keep my API as it is, since filtering with GET parameter ?date=15-09-2017
will just decrease the number of news in the API result.
But would it still be possible to just keep all the news in an array in a news
variable in my reducer or do I have to structure it to be something like
news: {
'15-09-2017': {
news: [...],
pagination: {}
},
...
}
in order to keep track of the pagination for every single date?
A think a structure where you store your your news by id and ids per date would be good and flexible structure:
{
"byId": {
"10": { /* News */ },
"14": { /* News */ },
/* ... */
},
"listsByDate": {
"2017-08-24": {
"total": 100,
"pageSize": 10,
"page": 2,
"items": [
10,
14,
/* ... */
]
}
}
}
You can implement simple selectors for this structure:
const getNewsById = (state, id) => state.byId[id];
const getListByDate = (state, date) => state.listByDate[date];
const getPageOfList = (state, data) => getListByDate(state, date).page
/* ... */
const getNewsByDate = (state, date) => {
return getListByDate(state, data).items.map((id) => {
return getNewsById(state, id);
});
}
I would recommend to keep storing all news in a single place (news
in your reducer), and use selectors to compute derived data.
To achieve this, you will need to store the current date (I suppose it's a some kind of filter in your app) in Redux store, so it can be used in your selector.
So your reducer will look like:
{
news: [], // you can keep all the news here
date: '15-09-2017', // selected date
page: 1, // current page
pagination: {
'15-09-2017': { ... }, // pagination info by date.
// You will need this only if you use the classic pager (with page numbers in UI)
// it's not needed in case of infinite scroll
}
}
And selectors:
import { createSelector } from 'reselect';
// select all news
const newsSelector = () => (state) => state.get('news');
// select date
const dateSelector = () => (state) => state.get('date');
// select page
const pageSelector = () => (state) => state.get('page');
// select news by date
const newsByDateSelector = () => createSelector(
newsSelector(),
dateSelector(),
(news, date, page) => news.filter((newsItem) => newsItem.get('date') === date)
);
const pageSize = 10;
const newsByDatePagedSelector = () => createSelector(
newsByDateSelector(),
pageSelector(),
(news, page) => news.slice(pageSize * (page - 1), pageSize * page)
);
Then you can use newsByDatePagedSelector
selector to get the needed news in your container:
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
export class NewsContainer extends React.PureComponent {
componentDidMount() {
// news by selected date are not loaded yet
if (!this.props.news) {
this.loadNews();
}
}
componentWillReceiveProps(nextProps) {
// user navigated to the next page
if (this.props.news && !nextProps.news) {
this.loadNews();
}
}
loadNews() {
// fetch next 10 news from server
}
render() {
return (
<div>
this.props.news.map((newsItem) => ...)
</div>
);
}
}
const mapStateToProps = createStructuredSelector({
news: newsByDatePagedSelector(),
});
export default connect(mapStateToProps)(NewsContainer);
When user reach the last news by some date, you can request next 10 news from your API as usual with filter ?date=15-09-2017
and put them to your store. Redux and reselect will bring them to NewsContainer
as newsByDate
prop.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With