Paginate date-specific результаты API с React и Redux
Я хочу показать некоторые новости в своем приложении React с помощью Redux.
Проблема в том, что я хочу показывать новости для отдельных дат, и я хочу разбивать новости на новости.
В моем API я печатаю
{
pagination: {
count: 1000,
size: 10,
page: 1,
pages: 100
},
news: [
..
]
}
Я знаю, как сделать простую разбивку на страницы, но я не знаю, как должен работать API, если я хочу иметь возможность показывать новости для разных дат в своем приложении.
До сих пор (без дат) я только что сохранил состояние news
и pagination
в моем редукторе Redux, а затем проверял, равен ли номер страницы общему количеству страниц, чтобы определить, следует ли попробовать загружать больше новостей.
Но теперь, когда у меня потенциально много разных дат, и я хочу сохранить все новости в магазине Redux, я не знаю, как его структурировать.
Я могу сохранить API как есть, так как фильтрация с параметром GET ?date=15-09-2017
просто уменьшит количество новостей в результате API.
Но будет ли возможно сохранять все новости в массиве в переменной news
в моем редукторе или мне нужно создать его как нечто вроде
news: {
'15-09-2017': {
news: [...],
pagination: {}
},
...
}
чтобы отслеживать разбивку на страницы для каждой отдельной даты?
Ответы
Ответ 1
Думайте, что структура, в которой вы храните свои новости по id и идентификаторы в день, будет хорошей и гибкой структурой:
{
"byId": {
"10": { /* News */ },
"14": { /* News */ },
/* ... */
},
"listsByDate": {
"2017-08-24": {
"total": 100,
"pageSize": 10,
"page": 2,
"items": [
10,
14,
/* ... */
]
}
}
}
Вы можете реализовать простые селектора для этой структуры:
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);
});
}
Ответ 2
Я бы рекомендовал хранить все новости в одном месте (news
в вашем редукторе) и использовать селекторы для вычисления полученных данных.
Для этого вам нужно будет сохранить текущую дату (я полагаю, это какой-то фильтр в вашем приложении) в хранилище Redux, поэтому ее можно использовать в вашем селекторе.
Итак, ваш редуктор будет выглядеть так:
{
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 not needed in case of infinite scroll
}
}
И селекторов:
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)
);
Затем вы можете использовать селектор newsByDatePagedSelector
для получения необходимых новостей в вашем контейнере:
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);
Когда пользователь достигает последних новостей к какой-то дате, вы можете запросить следующие 10 новостей из вашего API, как обычно, с помощью фильтра ?date=15-09-2017
и поместить их в свой магазин. Redux и reselect приведут их к NewsContainer
как newsByDate
prop.