TL;DR: How do you store pagination state in a Redux store given a large, normalized state tree with lots of resources, and multiple views displaying different paginated results?
I'm wondering what is the best way to store pagination data in a Redux store. This article prescribes storing pagination info as its own subtree within the state tree. I agree with that method, but here's where I disagree: the article suggests keying the pagination subtree by the type of resource being paginated, but what if there are several views that display paginated data of the same resource using different parameters? If you follow the article's method, your state tree may end up looking like this
{
pagination:
todos:
count: 1000,
nextPageUrl: ...,
result: [list of id's]
users:
count: 200,
nextPageUrl: ...,
result: [list of id's]
}
This doesn't seem to make sense to me if you have multiple ways to view the same resource. What if on one screen there are multiple tables displaying todos that were fetched using different parameters? For instance, one table displays todos that were fetched from /api/todos/?userId=1
and another displays todos from /api/todos/?userId=2
.
Given the above problem, it seems that a solution for allowing more flexible pagination is to store pagination state on a per-component basis, rather than a per-entity basis. So your state could look something like this
{
pagination:
componentId:
count: 1000,
nextPageUrl: ...,
result: [list of id's]
anotherComponentId:
count: 1000,
nextPageUrl: ...,
result: [list of id's]
}
Of course, storing pagination results like this loses context as to what is being paginated. But is that so much of a problem? How do you go about paginating a large state tree with potentially multiple paginated results of the same resource? Thanks!
I think that redux-cached-pagination (Demo here) can help to cope with your problem. Normalized search results are stored in redux in a tree like that:
{
standard_store: {
pagination: {
searchPhrase__: {//results for given search params
lastUpdateTime: 1516177824890,//last update time helps to refresh data in background
pages: { //each page is stored. You can adjust history length
'1': {
isFetching: false,//each page has fetching status
isRefreshing: false,
isSuccess: true,
isFailed: false,
lastUpdateTime: 1516177824891,
elements: [ //in 'elements' array only ids are stored. Results are normalized
100000,
100001,
...
]
}
},
elementsCount: 800
},
searchPhrase_a_: { //results for "a" search phrase
lastUpdateTime: 1516177844624,
pages: {
'1': {
isFetching: false,
isRefreshing: false,
isSuccess: true,
isFailed: false,
lastUpdateTime: 1516177844624,
elements: [
100001,
100016,
100017,
100031,
100044,
100046,
100049,
100055,
100056,
100058,
100065,
100076,
100077,
100084,
100088
]
}
},
elementsCount: 138
}
},
entities: { //here are stored entities. Entities are not duplicated.
'100000': {
examData: 'BVUGUN',
examIndexAtAll: 0,
id: 100000
},
'100001': {
examData: 'ZR7ADV',
examIndexAtAll: 1,
id: 100001
},
...
},
paginationParams: {},
searchParams: {
searchPhrase: 'al'
},
currentPage: {
pageNumber: 1
}
}
}
This json is copied directly from redux. Additionally you get features like refreshing results in background, simple use of virtualized list, storing last visited page and many others. If you want to use it in multiple lists I suggest to use reselect.
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