I have a custom list component called TableList
. When a user clicks on an arrow it should increment to the next data set. However, when the first click in either direction is made, the component only updates the page number, not the data.
For example: when on page one, clicking next
increments to page two, but the data remains the same. Clicking again, increments both the page number and the data. It will continue to paginate correctly, until the user starts clicking previous
. Then it will give the same behavior as before for the first click, and continue normally for subsequent clicks.
The primary pagination function:
function changePage(direction) {
if (
(currentPage === 0 && direction === 'previous') ||
currentPage * pageSize > data
) {
return null;
}
if (direction === 'previous' && currentPage > 0) {
const newPage = currentPage - 1;
setDataSlice(data.slice(newPage * pageSize, currentPage * pageSize));
setCurrentPage(newPage);
}
if (direction === 'next') {
const newPage = currentPage 1;
setDataSlice(data.slice(currentPage * pageSize, newPage * pageSize));
setCurrentPage(newPage);
}
}
Full code:
import {faArrowRight, faArrowLeft} from '@fortawesome/free-solid-svg-icons';
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome';
import {func, number, string, shape} from 'prop-types';
import React, {useState} from 'react';
import {View, Text, FlatList} from 'react-native';
import EtherButton from '../components/EtherButton';
import {useTheme} from '../context/ThemeContext';
PageMenu.propTypes = {
onNext: func.isRequired,
onPrev: func.isRequired,
page: number.isRequired,
};
function PageMenu({onNext, onPrev, page}) {
const {style, values} = useTheme(getThemedStyles);
return (
<View style={style.pageMenuContainer}>
<EtherButton style={style.arrowButton} onPress={onPrev}>
<FontAwesomeIcon icon={faArrowLeft} color={values.BGSECOND} size={30} />
</EtherButton>
<View>
<Text style={style.counter}>{page}</Text>
</View>
<EtherButton style={style.arrowButton} onPress={onNext}>
<FontAwesomeIcon
icon={faArrowRight}
color={values.BGSECOND}
size={30}
/>
</EtherButton>
</View>
);
}
export default function TableList({
data,
pageSize,
style: overrides,
pageView = true,
}) {
const {style} = useTheme(getThemedStyles);
const [loadCount, setLoadCount] = useState(pageSize);
const [currentPage, setCurrentPage] = useState(0);
const [dataSlice, setDataSlice] = useState(data.slice(0, pageSize));
ListItem.proptypes = {
itemData: shape({
date: string.isRequired,
name: string.isRequired,
orderNumber: string.isRequired,
total: number.isRequired,
}),
};
function ListItem({orderData}) {
const [mouseHover, setMouseHover] = useState(false);
const textHighlight = [
style.valueText,
mouseHover ? style.textHighlighted : null,
];
return (
<View
onm ouseOver={() => setMouseHover(true)}
onm ouseLeave={() => setMouseHover(false)}
style={[style.listRow, mouseHover ? style.rowHighlighted : null]}
>
<View style={style.amountCell}>
<Text style={textHighlight}>{orderData.total} </Text>
</View>
<View style={style.nameCell}>
<Text style={textHighlight}>{orderData.name}</Text>
</View>
<View style={style.tokenCell}>
<Text style={textHighlight}>{orderData.orderNumber}</Text>
</View>
<View style={style.dateCell}>
<Text style={textHighlight}>
{new Date(orderData.date).toLocaleString()}
</Text>
</View>
</View>
);
}
function loadMore() {
const newCount = loadCount pageSize;
setLoadCount(newCount);
setDataSlice(data.slice(0, newCount));
}
function changePage(direction) {
if (
(currentPage === 0 && direction === 'previous') ||
currentPage * pageSize > data
) {
return null;
}
if (direction === 'previous' && currentPage > 0) {
const newPage = currentPage - 1;
setDataSlice(data.slice(newPage * pageSize, currentPage * pageSize));
setCurrentPage(newPage);
}
if (direction === 'next') {
const newPage = currentPage 1;
setDataSlice(data.slice(currentPage * pageSize, newPage * pageSize));
setCurrentPage(newPage);
}
}
return (
<View style={[style.mainContainer, overrides]}>
<View style={style.topRow}>
<View style={style.amountCell}>
<Text style={style.headerText}>Price</Text>
</View>
<View style={style.nameCell}>
<Text style={style.headerText}>Description</Text>
</View>
<View style={style.tokenCell}>
<Text style={style.headerText}>Order ID</Text>
</View>
<View style={style.dateCell}>
<Text style={style.headerText}>Date</Text>
</View>
</View>
<FlatList
data={dataSlice}
key={dataSlice}
renderItem={({item}) => <ListItem orderData={item} />}
keyExtractor={(item) => item.orderNumber}
style={style.flatList}
/>
<Text style={style.timeZone}>
Time shown in {new Date().toString().match(/([A-Z] [-][0-9] .*)/)[0]}
</Text>
<View style={style.bottomBar}>
{pageView ? (
<PageMenu
onPrev={() => changePage('previous')}
onNext={() => changePage('next')}
page={currentPage 1}
/>
) : (
<EtherButton onPress={loadMore} style={style.loadMoreButton}>
<Text style={style.buttonText}>Load More</Text>
</EtherButton>
)}
</View>
</View>
);
}
const getThemedStyles = (theme, fontSize) => ({
mainContainer: {
backgroundColor: theme.BGFIRST,
borderColor: theme.FIRST,
borderWidth: 2,
borderRadius: 5,
overflowX: 'auto',
},
topRow: {
backgroundColor: theme.SECOND,
height: 40,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
borderTopLeftRadius: 2,
borderTopRightRadius: 2,
},
headerText: {
fontFamily: 'NotoSans_Regular',
fontSize: fontSize.body,
color: theme.LIGHT,
alignSelf: 'center',
},
flatList: {
paddingVertical: 20,
},
pageMenuContainer: {
flexDirection: 'row',
alignItems: 'center',
},
counter: {
fontFamily: 'NotoSans_Regular',
fontSize: fontSize.body,
backgroundColor: theme.BGSECOND,
color: theme.DARK,
padding: 5,
borderRadius: 5,
borderColor: theme.FIRST,
borderWidth: 2,
},
arrowButton: {
borderColor: theme.SECOND,
},
bottomBar: {
alignItems: 'center',
backgroundColor: theme.SECOND,
},
loadMoreButton: {
justifyContent: 'center',
alignItems: 'center',
height: 40,
marginVertical: 5,
width: '15%',
borderRadius: 5,
borderColor: theme.FIRST,
borderWidth: 2,
backgroundColor: theme.BGSECOND,
},
loadMoreText: {
fontFamily: 'NotoSans_Regular',
fontSize: fontSize.subheader,
},
buttonText: {
fontFamily: 'NotoSans_Bold',
fontSize: fontSize.legal,
color: theme.FIRST,
textAlign: 'center',
},
// Table
listRow: {
alignItems: 'center',
flexDirection: 'row',
height: 33,
justifyContent: 'space-around',
},
rowHighlighted: {
backgroundColor: theme.SECOND,
},
valueText: {
alignSelf: 'center',
fontFamily: 'NotoSans_Regular',
fontSize: fontSize.legal,
},
textHighlighted: {
color: theme.LIGHT,
},
amountCell: {
width: 80,
minWidth: 60,
},
nameCell: {
width: 220,
minWidth: 210,
},
tokenCell: {
width: 200,
minWidth: 150,
},
dateCell: {
width: 140,
minWidth: 115,
},
//
timeZone: {
alignSelf: 'center',
fontFamily: 'NotoSans_Bold',
fontSize: fontSize.legal,
color: theme.DARK,
marginBottom: 20,
},
});
CodePudding user response:
Could you please try putting changePage into a useCallback and providing dataSlice as a dependency? It looks like a data leak to me.
Unrelated: you shouldn't use key if you are using the keyExtractor. I would remove the key prop from the FlatList.
CodePudding user response:
I'm embarrassed that it took me this long to figure it out, but I found the answer. I was basing the data set off the wrong variables. Changing the setState in the if (direction === "next")
section fixed it. Here's what I changed it to:
setDataSlice(data.slice(newPage * pageSize, (newPage 1) * pageSize));