Last active
March 20, 2020 16:43
-
-
Save sgobotta/9be0ff2e8ff303ef0715cc77e14a7541 to your computer and use it in GitHub Desktop.
A react gist for paginated grids, using react hooks.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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