typescriptoptimizationreact-native-flatlistnative-base

Optimization of a flatlist


I'm trying to make a video and photo feed, for that I use the flatlist tool and an event card component. I can visualize my videos. But it is not optimized. And I have this problem.

VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc. {"contentLength": 3816, "dt": 3686, "prevDt": 936}

myFlatlist

const EVENTS = graphql(
  `
    query events {
      events {
        id
        category
        description
        name
        file {
          url
        }
        author {
          username
          avatar {
            url
          }
        }
      }
    }
  `
);
export default function ReelForyouScreen({
  navigation,
}: RootTabScreenProps<"Foryou">) {
  const { loading, error, data } = useQuery(EVENTS);
  const [focusedIndex, setFocusedIndex] = React.useState(0);
  const handleScroll = React.useCallback(
    ({
      nativeEvent: {
        contentOffset: { y },
      },
    }: NativeSyntheticEvent<NativeScrollEvent>) => {
      const offset = Math.round(y / 500);

      setFocusedIndex(offset);
    },
    [setFocusedIndex]
  );

  const verification = (event: Event, index: int) => {
    const parts: string[] = event?.file.url.split(".");
    const extension: string = parts[parts.length - 1];
    if (extension === "mp4") {
      return <EventCard event={event} shouldPlay={focusedIndex === index} />;
    } else {
      return <EventCardPhoto event={event} />;
    }
  };

  return (
    <Box bg={loading ? "#000" : "#000"}>
      <FlatList
        py={4}
        mx={1}
        removeClippedSubviews
        initialNumToRender={5}
        maxToRenderPerBatch={10}
        windowSize={10}
        refreshing={loading}
        onScroll={handleScroll}
        data={data?.events as Event[]}
        renderItem={({ item, index }) => verification(item, index)}
      />
    </Box>
  );
}

and my component

type Props = {
  event: Event;
  shouldPlay: boolean;
  imageState?: StateModifier<string | null>;
};

const EventCard = ({ event, shouldPlay }: Props) => {
  const [liked, setLiked] = useState(false);
  const handlePress = () => {
    setLiked(!liked);
  };
  const video = useRef<Video>(null);

  const [isLoading, setIsLoading] = useState(true);

  const onVideoLoad = () => {
    setIsLoading(false);
  };
  const reel = {
    reelimage:
      "https://weezevent.com/wp-content/uploads/2019/03/01184934/organiser-soiree.jpeg",
    username: "Matteo.hmi",
    evenements: "11",
    date: "2023-04-02T08:02:17-05:00",
    followers: "255",
    userimage:
      "https://media.licdn.com/dms/image/D5603AQEh2KcqHOfMgw/profile-displayphoto-shrink_800_800/0/1666676898390?e=2147483647&v=beta&t=x88KFJJJJOLex9X3XQTwaEs-2KCodrovN76Mp6ZsKC0",
    numberLike: "255",
    numberComment: "355",
    numberShare: "50",
    numberTicketDisponible: "20",
    reelDescription:
      "Ne ratez pas le prochain évènement de l’Arc Paris ce samedi à 10:00 PM.",
  };
  const iconsInteraction: { name: string; iconLibrary: any; type: any }[] = [
    {
      name: "chatbubble-ellipses",
      iconLibrary: MaterialCommunityIcons,
      type: reel.numberComment,
    },
    { name: "arrow-redo", iconLibrary: Ionicons, type: reel.numberShare },
    {
      name: "bookmark",
      iconLibrary: Fontisto,
      type: reel.numberTicketDisponible,
    },
  ];
  return (
    <Box
      rounded="3xl"
      bg="transparent"
      h={500}
      shadow={3}
      mb={8}
      overflow="hidden"
    >
      {isLoading && (
        <Box
          w="100%"
          h="100%"
          bg="black"
          alignItems="center"
          justifyContent="center"
        >
          <ActivityIndicator size="large" color="#fff" />
        </Box>
      )}
      <Video
        ref={video}
        source={{
          uri: event?.file.url,
        }}
        style={{
          width: "100%",
          height: "100%",
          position: "absolute",
        }}
        useNativeControls={false}
        isLooping
        onLoad={onVideoLoad}
        resizeMode="cover"
        usePoster
        posterSource={{
          uri: event?.file.url,
        }}
        shouldPlay={shouldPlay}
        on
        isMuted={false}
      />
      <VStack h="100%" justifyContent="space-between">
        <Pressable
          style={{ alignSelf: "flex-start", paddingTop: 18, paddingLeft: 18 }}
        >
          <HStack space={2} alignItems="center">
            <Avatar
              size={10}
              source={{
                uri:
                  event?.author.avatar === null
                    ? "https://media.licdn.com/dms/image/C4D03AQHsbDsDTukBaw/profile-displayphoto-shrink_400_400/0/1632175934748?e=1684972800&v=beta&t=JIsEqBO-2kmrjeS8BC9xGx8EfU4Bshbn99D3nGALV64"
                    : event?.author.avatar?.url,
              }}
            />

            <Text color="white" fontWeight="blod">
              {event?.author.username}
            </Text>
          </HStack>
        </Pressable>
        <VStack space={16} px={18}>
          <VStack alignSelf="flex-end" space={1}>
            <Pressable alignItems="center" onPress={handlePress}>
              <Animatable.View
                animation={liked ? "rubberBand" : ""}
                style={{ width: "100%", alignItems: "center" }}
              >
                <Ionicons
                  alignSelf="center"
                  name="heart"
                  size={40}
                  color={liked ? "#ff2d55" : "white"}
                  onpPress={handlePress}
                />
              </Animatable.View>
            </Pressable>
            {iconsInteraction.map((icon, index) => (
              <Container flexDirection="column" alignItems="center" key={index}>
                <Icon as={Ionicons} name={icon.name} size={8} color="white" />
                <Text color="white" fontWeight="bold">
                  {icon.type}
                </Text>
              </Container>
            ))}
          </VStack>

          <Box bottom={6} left={4} shadow={1}>
            <Text color="white" fontSize={15} fontWeight="bold">
              {event?.description}
            </Text>
          </Box>
        </VStack>
      </VStack>
    </Box>
  );
};

export default memo(EventCard);

I would like it to work in an optimal way but I can't.


Solution

  • Try FlashList from shopify, they created a new FlatList with a better optimization.

    https://shopify.github.io/flash-list/