Skip to content

Instantly share code, notes, and snippets.

@sgobotta
Last active March 20, 2020 16:43
Show Gist options
  • Save sgobotta/9be0ff2e8ff303ef0715cc77e14a7541 to your computer and use it in GitHub Desktop.
Save sgobotta/9be0ff2e8ff303ef0715cc77e14a7541 to your computer and use it in GitHub Desktop.
A react gist for paginated grids, using react hooks.
export default function GridScreen() {
const [isFetchingComplete, setFetchingComplete] = React.useState(false);
const [tweetsState, setTweetsState] = React.useState({
// initialises an empty array for tweets results
results: [],
})
// The API url should go here. E.g: http://localhost:8000
const hostUrl = '...'
// descendant order, depends on the node backend implementation - @sgobotta
const sortParam = `sort=-created_at`
// the resource service and page param depend on the backend implementation
const firstPageUrl = `${hostUrl}/api/tweets/?page=1&${sortParam}`
const [apiState, setApiState] = React.useState({
nextPageUrl: firstPageUrl
})
function getFilteredTweets(tweets) {
const { results: currentList } = tweetsState
const tweetIds = tweets.map(e => e.id)
const currentListIds = currentList.map(e => e.id)
return tweetIds.reduce(
(accumulator, nextValue, index) => {
if (!currentListIds.includes(nextValue)) {
accumulator.push(tweets[index])
}
return accumulator
}, []
)
}
async function fetchTweets(url) {
const response = await fetch(url);
return await response.json();
}
async function onRefreshFeed() {
try {
// Fetches tweets
const tweetsResponse = await fetchTweets(firstPageUrl)
// Filters new tweets against those previously fetched
// Assumes the backend answers with a 'results' attribute with an array
// of tweets
const { results: responseResults } = tweets
const filteredTweets = getFilteredTweets(responseResults)
// Updates the elements to the feed state
setTweetsState({
...tweetsState,
results: filteredTweets.concat(tweetsState.results)
})
} catch(err) {
console.warn(err)
} finally {
setFetchingComplete(true)
}
}
async function getFeed() {
try {
const { nextPageUrl } = apiState
if (nextPageUrl === null) {
return;
}
// Assumes the backend answers with a 'results' attribute with an array
// of tweets
const { results: currentTweets } = tweetsState
// Fetches tweets
const tweetsResponse = await fetchTweets(apiState.nextPageUrl)
// Filters new tweets against those previously fetched
const {
results: responseResults,
nextPageUrl: updatedNextPageUrl
} = tweetsResponse
const filteredTweets = getFilteredTweets(responseResults)
// Adds elements to the feed state
const updatedList = currentTweets.concat(filteredTweets)
setTweetsState({ results: updatedList })
// Updates api state with the next page url
setApiState({ nextPageUrl: updatedNextPageUrl })
} catch (err) {
console.warn(err)
} finally {
setFetchingComplete(true)
}
}
React.useEffect(() => {
getFeed()
}, [])
return (
// The twitter avatars Grid should go here
//
// The current implementation is valid only for react native elements
<View style={styles.container}>
<SafeAreaView>
<FlatList
data={tweetsState.results}
keyExtractor={item => `${item.id}`}
onEndReached={getFeed}
onRefresh={onRefreshFeed}
refreshing={!isFetchingComplete}
renderItem={({ item }) => (
<Item
description={item.description}
id={item.id}
pictureUrl={`${hostUrl}/${item.picture_file}`}
title={item.title}
/>
)}
/>
</SafeAreaView>
</View>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment