warning
Before assessing your list's performance, make sure you are in release mode. On Android, you can disable JS dev mode inside the developer menu, whereas you need to run the release configuration on iOS. FlashList can appear to be slower than FlatList in dev mode. The primary reason is a much smaller and fixed window size equivalent. Click here to know more about why you shouldn't profile with dev mode on.
Memoizing props passed to FlashList is more important in v2. v1 was more selective about updating items, but this was often perceived as a bug by developers. We will not follow that approach and will instead allow developers to ensure that props are memoized. We will stop re-renders of children wherever it is obvious.
Writing Performant ComponentsWhile FlashList
does its best to achieve high performance, it will still perform poorly if your item components are slow to render. In this post, let's dive deeper into how you can remedy this.
One important thing to understand is how FlashList
works under the hood. When an item gets out of the viewport, instead of being destroyed, the component is re-rendered with a different item
prop. When optimizing your item component, try to ensure as few things as possible have to be re-rendered and recomputed when recycling.
There's lots of optimizations that are applicable for any React Native component and which might help render times of your item components as well. Usage of useCallback
, useMemo
, and useRef
is advised - but don't use these blindly, always measure the performance before and after making your changes.
note
Always profile performance in the release mode. FlashList
's performance between JS dev and release mode differs greatly.
key
prop
warning
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
.
FlashList's core performance advantage comes from recycling components instead of creating and destroying them however, when you add a key
prop that changes between different data items, React treats the component as entirely different and forces a complete re-creation of the component tree.
For example, if we had a following item component:
const MyNestedComponent = ({ item }) => {
return <Text key={item.id}>I am nested!</Text>;
};
const MyItem = ({ item }) => {
return (
<View key={item.id}>
<MyNestedComponent item={item} />
<Text>{item.title}</Text>
</View>
);
};
Then the key
prop should be removed from both MyItem
and MyNestedComponent
. It isn't needed and react can alredy take care of updating the components.
const MyNestedComponent = ({ item }) => {
return <Text>I am nested!</Text>;
};
const MyItem = ({ item }) => {
return (
<View>
<MyNestedComponent item={item} />
<Text>{item.title}</Text>
</View>
);
};
There might be cases where React forces you to use key
prop, such as when using map
. In such circumstances, use useMappingHelper
to ensure optimal performance:
import { useMappingHelper } from "@shopify/flash-list";
const MyItem = ({ item }) => {
const { getMappingKey } = useMappingHelper();
return (
<>
{item.users.map((user, index) => (
<Text key={getMappingKey(user.id, index)}>{user.name}</Text>
))}
</>
);
};
The useMappingHelper
hook intelligently provides the right key strategy:
This approach ensures that:
info
useMappingHelper
should be used whenever you need to map over arrays inside FlashList item components. It automatically handles the complexity of providing recycling-friendly keys.
If you do any calculations that might take a lot of resources, consider memoizing it, making it faster, or removing it altogether. The render method of items should be as efficient as possible:
getItemType
If you have different types of cell components and these are vastly different, consider leveraging the getItemType
prop. For example, if we were building a messages list, we could write it like this:
enum MessageType {
Text,
Image,
}
interface TextMessage {
text: string;
type: MessageType.Text;
}
interface ImageMessage {
image: ImageSourcePropType;
type: MessageType.Image;
}
type Message = ImageMessage | TextMessage;
const MessageItem = ({ item }: { item: Message }) => {
switch (item.type) {
case MessageType.Text:
return <Text>{item.text}</Text>;
case MessageType.Image:
return <Image source={item.image} />;
}
};
const MessageList = () => {
return <FlashList renderItem={MessageItem} />;
};
However, this implementation has one performance drawback. When the list recycles items and the MessageType
changes from Text
to Image
or vice versa, React won't be able to optimize the re-render since the whole render tree of the item component changes. We can fix this by changing the MessageList
to this:
const MessageList = () => {
return (
<FlashList
renderItem={MessageItem}
estimatedItemSize={200}
getItemType={(item) => {
return item.type;
}}
/>
);
};
FlashList
will now use separate recycling pools based on item.type
. That means we will never recycle items of different types, making the re-render faster.
Let's consider the following example:
const MyHeavyComponent = () => {
return ...;
};
const MyItem = ({ item }) => {
return (
<>
<MyHeavyComponent />
<Text>{item.title}</Text>
</>
);
};
Since MyHeavyComponent
does not directly depend on the item
prop, memo
can be used to skip re-rending MyHeavyComponent
when the item is recycled and thus re-rendered:
const MyHeavyComponent = () => {
return ...;
};
const MemoizedMyHeavyComponent = memo(MyHeavyComponent);
const MyItem = ({ item }: { item: any }) => {
return (
<>
<MemoizedMyHeavyComponent />
<Text>{item.title}</Text>
</>
);
};
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4