The problem: I have a FlashList that uses React Context to fill in the data (the data is an array of objects that renders a View) but when I update the context and the extraData
prop for FlashList, the list does not re-render, or re-renders sometimes, or takes multiple events to actually re-render.
The Code:
// Many imports, they are all fine though
export default () => {
// Relevant context.
const {
cardsArray,
cardsArrayFiltered,
updateCardsArray,
updateCardsArrayFiltered
} = useContext(AppContext);
// Relevant state.
const [didUpdateCards, setDidUpdateCards] = useState(false);
const [cardsFilters, setCardsFilters] = useState([]);
// Relevant refs.
const flatListRef = useRef(null);
// Example effect on mount
useEffect(() => {
setInitialAppState();
}, []);
// Effect that listen to changing on some data that update the context again
useEffect(() => {
const newCardsArray = doSomeFiltering(cardsArray, cardsFilters);
updateCardsArrayFiltered(newCardsArray);
setDidUpdateCards(!didUpdateCards);
}, [cardsFilters]);
// Example of promisey function that sets the initial context.
const setInitialAppState = async () => {
try {
const newCardsArray = await getPromiseyCards();
updateCardsArrayFiltered(newCardsArray);
updateCardsArray(newCardsArray);
} catch ( err ) {
console.debug( err );
}
}
// Renderer for the list item.
const renderListItem = useCallback((list) => <Card key={list.index} card={list.item} />, []);
// List key extractor.
const listKeyExtractor = useCallback((item) => item.id, []);
return (
<FlashList
ref={flatListRef}
data={cardsArrayFiltered}
extraData={didUpdateCards}
keyExtractor={listKeyExtractor}
renderItem={renderListItem}
showsVerticalScrollIndicator={false}
estimatedItemSize={Layout.window.height}
/>
);
}
Notes:
- What I did not write all out is the function, logic, view to update
cardsFilters
however the above effect IS running when it changes. - Moreover, this line here,
const newCardsArray = doSomeFiltering(cardsArray, cardsFilters);
does indeed return the proper updated data.
What’s going on here? I am updating the extraData
prop with that didUpdateCards
state when the context changes which I thought was the requirement to re-render a FlatList/FlashList.
2
Answers
It looks like object being passed as
extraData
is a boolean. This means that if the previous value was true, setting it as true again wouldn’t count as a change. Instead use an object and update it when you want list to update.To try just set
extraData={{}}
. if everything works as expected it means that your update logic has some problem.@Staghouse your component contains key property, and official docs recommend to remove it: https://shopify.github.io/flash-list/docs/fundamentals/performant-components/#remove-key-prop :
"Using key prop inside your item and item’s nested components will highly degrade performance.
Make sure your item components and their nested components don’t have a key prop. Using this prop will lead to FlashList not being able to recycle views, losing all the benefits of using it over FlatList."
Hope this helps.