
PaginatableList
PaginatableList is a list component developed by TTT Studio Mobile Team for the purpose of avoiding repetitive logic that handles loading more list items via pagination in the REST API, and pull-to-refresh the entire list. PaginatableList is built on top of React Native's FlatList
, but we make it be able to manage the list items automatically and store the list items in Redux Store.
By default, it provides:
- Reaching the list bottom to load more items after making GET API call.
- Pull-to-refresh the whole list.
However, it is customizable regarding the item cell appearance, and more actions than the two default actions out of the box.

Installation
yarn add @twotalltotems/paginatable-list
or npm install @twotalltotems/paginatable-list --save
- Install dependencies.
Dependencies
This project needs the follow dependencies inside your project.
react-redux
reduxsauce
redux-thunk
prop-types
axios
react-native-config
Basic Usage
In order to use PaginatableList
, first create a PaginationStateManager
instance. There are two required parameters:
import { PaginationStateManager } from '@twotalltotems/paginatable-list';
import Config from 'react-native-config';
const paginationStateManager = new PaginationStateManager('users', `${Config.BASE_API_URL}/users`);
Parameter | Description |
---|
key | Redux store key that will be used to store list items. In this example, users is the key that will be used to store the item list in the Redux store. |
endpointUrl | The paginatable endpoint URL for requesting content for each page with pageNumber and pageSize. In this example, ${Config.BASE_API_URL}/users is the endpointUrl. Config.BASE_API_URL is stored in .env file. |
Link Redux Store
import { combineReducers, applyMiddleware, compose} from 'redux';
import Reactotron from 'reactotron-react-native'
const getCombinedReducers = () => {
return combineReducers({
users : paginationStateManager.reducer(), //'users' should be kept the same as the first param in PaginationStateManager instance created in the last step.
});
}
const reduxStore = Reactotron.createStore(
getCombinedReducers(),
{}, //Initial State of Redux Store
compose(
applyMiddleware(ReduxThunk)
)
)
Notice: The key used in combineReducers
need to be consistant with the key that is used to initialize PaginationStateManager
in the last step, which in this example, is users
.
Use PaginatableList Component
Inside your component, you can use this code.
render() {
return (
<View style={{ flex:1 }}>
<PaginatableList
onRenderItem={this.renderListItem}
customizedPaginationStateManager={paginationStateManager}
/>
</View>
)
}
renderListItem = ({ index, item }) => {
return (
<TouchableOpacity>
<Text>User ID:{item.id}</Text>
<Text>Email: {item.email}</Text>
</TouchableOpacity>
)
}
renderListItem
is used to render specific list item. PaginatableList exists as the scrollable container of the list items that will complete the two most common operations, loading more items and pull-to-refresh, for you. With this in mind, you can use this PaginatableList container to holder whatever you prefer. It could be View
, TouchableOpacity
, or your customized component.- In this example,
renderListItem
params item
is the object within the array that is stored in Redux store. Currently, PaginationStateManager
is storing whatever the server response. In this example, local server is return an array of objects, and each object contains id
, email
, and password
. Therefore, Redux store is storing by default the exact same format of the object.
Customization
If you need more than loading more items, and pull-to-refresh, continue reading.
For many lists in real life situations, yu would need more than only two common operations for the list. Example for an extra operations could be click to like an item, delete an item, or highlight an item. Below there's an explanation using highlighting an item as example.
To customize, first subclass from PaginatableListReducer
.
import { PaginationStateManager } from '@twotalltotems/paginatable-list';
export default class CustomizedPaginationStateManager extends PaginationStateManager {
constructor(name, url) {
super(name, url)
}
}
Initialize instance of CustomizedPaginationStateManager, and add extra action to it.
export const customizedPaginationStateManager = new CustomizedPaginationStateManager('customized_users', 'users')
customizedPaginationStateManager.addActions([
{
type: 'highlightItem',
payload: ['index'],
handler: (state, { index }) => {
return {
...state,
highlightedItemIndex: index
}
}
}
])
First, let's make the list item clickable, and make it call onHighlightItem
when the item is clicked.
renderListItem = ({ index, item }) => {
return (
<TouchableOpacity
onPress={() => {
this.onHighlightItem(index)
}}
>
<Text>User ID:{item.id}</Text>
<Text>Email: {item.email}</Text>
</TouchableOpacity>
)
}
onHighlightItem = (index) => {
this.props.dispatch(customizedPaginationStateManager.highlightItem({ index: index }))
}
render() {
return (
<UserListComponent
paginatableListReducer={customizedPaginationStateManager}
onHighlightItem={this.onHighlightItem}
/>
)
}
Overwrite the Extra Action Handler
Whenever you add extra actions to the subclass instance of PaginationStateManager
, this library will generate a default handler for you which will dispatch the action directly for you. However, if you would like to do more before dispatching the action, for example, make an API call, you can overwrite the default handler.
import { PaginationStateManager } from '@twotalltotems/paginatable-list';
export default class CustomizedPaginationStateManager extends PaginationStateManager {
constructor(name, url) {
super(name, url)
}
highlightItem = ({ index, extra }) => {
console.log('Overwrite Default highlightItem() function. For example, you might need to do network call before dispatch an action.')
// This is where you could add the API call.
return (dispatch) => {
dispatch(this.actions.highlightItem(index, extra))
}
};
}
Customize Empty Status
Customize empty status for PaginatableList via onRenderEmptyStatus
props.
renderEmptyStatus = () => {
return (
<View style={{ flex:1, alignItems: 'center', justifyContent: 'center' }}>
<View>
<Image
style={style.emptyStatusLogo}
resizeMode={'contain'}
source={require('../../Assets/TTTLogo.png')}
/>
<Text>Customized Empty Status</Text>
</View>
</View>
)
}
render() {
return (
<View style={{ flex:1 }}>
<PaginatableList
onRenderItem={this.renderListItem}
onRenderEmptyStatus={this.renderEmptyStatus}
customizedPaginationStateManager={this.props.paginatableListReducer}
/>
</View>
)
}
RoadMap