Sansaa59
Sansaa59

Reputation: 1

Prevent unnecessary re-renders when updating search state in React Native with TanStack Query

I’m developing a screen in React Native that lists books fetched from my API using TanStack Query. I’ve also implemented a search functionality that updates the results based on the entered text (debounced).

The issue is that when the search state changes, the items already in the list re-renders and blinks. I want to prevent this behavior and ensure that items already in the list do not re-render even when the search changes.

Relevant Code:

Main component (HomeScreen):

const HomeScreen = () => {
    const {
        data: books,
        isLoading,
        refetch
    } = useQuery < Book[] > ({
        queryKey: ["books"],
        queryFn: () => getBooks(),
        staleTime: 1000 * 60 * 5,
    });

    const search = useNavigationSearch({
        placeholder: "Search Books"
    });
    const {
        data: booksSearch,
        isLoading: isLoadingSearch
    } = useQuery < Book[] > ({
        queryKey: ["books", search],
        queryFn: () => searchBookByName(search),
        enabled: !!search && search !== "",
    });

    if (isLoading || isLoadingSearch || books === undefined) {
        return < ActivityIndicator size = {
            30
        }
        color = {
            "black"
        }
        />;
    }

    return ( <
        ScrollView >
        <
        Button title = "Refetch"
        onPress = {
            () => refetch()
        }
        /> <
        BookList simpleData = {
            books
        }
        searchData = {
            booksSearch
        }
        search = {
            search
        }
        /> < /
        ScrollView >
    );
};

BookList Component:

interface BookListProps {
    simpleData: Book[];
    searchData ? : Book[];
    search: string;
}

export default function BookList({
    simpleData,
    searchData,
    search,
}: BookListProps) {
    const hasSearch = !!search && search !== "";

    const shownData = React.useMemo(
        () => (hasSearch ? searchData : simpleData),
        [hasSearch, searchData, simpleData]
    );

    return ( <
        View style = {
            styles.container
        } >
        <
        FlatList contentContainerStyle = {
            styles.contentContainer
        }
        data = {
            shownData
        }
        renderItem = {
            ({
                item
            }: {
                item: Book
            }) => ( <
                BookCard book = {
                    item
                }
                key = {
                    item.id.toString()
                }
                />
            )
        }
        keyExtractor = {
            (book) => {
                const id = book.id.toString();
                console.log("extractId: ", id);
                return id;
            }
        }
        scrollEnabled = {
            false
        }
        /> <
        /View>
    );
}

Card Component:

interface BookCardProps {
    book: Book;
}

const BookCard = memo(
    ({
        book
    }: BookCardProps) => {
        const cardImage = book.image ?
            book.image :
            "https://cdn-icons-png.flaticon.com/512/1375/1375106.png";

        const description = book.description;
        const navigation = useNavigation < NavigationProp < RootHomeStackParamList >> ();

        return ( <
            Pressable style = {
                styles.pressable
            }
            onPress = {
                () => navigation.navigate("BookDetails", {
                    bookId: book.id
                })
            } >
            <
            FastImage style = {
                styles.image
            }
            source = {
                {
                    uri: cardImage
                }
            }
            /> <
            BookLikeButton book = {
                book
            }
            /> <
            View style = {
                styles.insideContainer
            } >
            <
            ThemedText style = {
                styles.title
            } > {
                book.name
            } < /ThemedText> <
            LocationTag style = {
                {
                    marginTop: 5
                }
            }
            ubi = {
                book.location
            }
            /> <
            ThemedText numberOfLines = {
                2
            }
            style = {
                styles.desc
            } > {
                description
            } <
            /ThemedText> <
            View style = {
                styles.properties
            } >
            <
            DateTag start = {
                book.startDate
            }
            end = {
                book.endDate
            }
            /> <
            /View> <
            /View> <
            /Pressable>
        );

    },
    (prevProps, nextProps) => {
        return prevProps.book.id === nextProps.book.id;
    }
);

Question: How can I optimize my implementation to prevent the items in the list from flickering when the search changes?

Thanks in advance for any help!

I’m using React.memo in the BookCard component to prevent unnecessary renders. I’ve also implemented a custom comparison in React.memo to check if the id of the book has changed:

I have logged the book.id from the KeyExtractor of the FlatList in BookList and they are the same

Upvotes: 0

Views: 31

Answers (1)

Chad S.
Chad S.

Reputation: 6631

If you want to keep the previous data while loading you need to supply a placeHolderData function.

e.g.

const {
        data: booksSearch,
        isLoading: isLoadingSearch
    } = useQuery({
        queryKey: ["books", search],
        queryFn: () => searchBookByName(search),
        placeHolderData: data => data,   // This will keep the old data 
                                         // in place while new data loads.
        enabled: !!search && search !== "",
    });

You probably also want to debounce your state for the search otherwise you'll spam your API with partial searches

Upvotes: 0

Related Questions