I have a couple of screens in my react native app which display search forms and the list of results. I'd like them to only run the search when the submit button is clicked, but currently the search runs as soon as the page is focussed and on any new letter in the form inputs.
Here's my screen component.
import React, { useContext, useState } from "react";
import { View, Text, TouchableOpacity, FlatList } from "react-native";
import GeneralStyles from "../../styles/general";
import FormStyles from "../../styles/forms";
import { TextInput } from "react-native-gesture-handler";
import SeriesCard from "../../components/bookCard";
import Toast from 'react-native-simple-toast';
import { useSearchListsQuery } from "../../store/listSlice";
import { AuthContext } from "../auth/AuthContext";
export default function IndexBooks({ navigation }) {
const { user } = useContext(AuthContext);
/**
* prepare form
*/
const [listNameSearch, setListNameSearch] = useState('');
/**
* load lists from search
*/
const [lists, setLists] = useState([]);
const handleSearch = () => {
refetchLists({ listNameSearch, page: 1 });
}
const {
data: data = [],
isLoading: isLoadingLists,
refetch: refetchLists,
isFetching: isFetchingLists,
isSuccess: isSuccessLists,
isError: isErrorLists,
error: errorLists,
} = useSearchListsQuery({ listNameSearch: listNameSearch, page: 1 });
if (isLoadingLists) {
Toast.show('Searching...');
console.log('loading lists');
} else if (isSuccessLists) {
setLists(data.data);
console.log('success loading lists', lists);
} else if (isErrorLists) {
console.log('Lists error', errorLists);
Toast.show(errorLists.toString());
}
return (
<View style={GeneralStyles.screen}>
{lists &&
<FlatList
data={lists}
ListHeaderComponent={
<View style={FormStyles.card}>
<TextInput
style={ FormStyles.input }
placeholder="List Name"
value={listNameSearch}
onChangeText={setListNameSearch}
/>
<TouchableOpacity
style={FormStyles.button}
onPress={handleSearch}
>
<Text style={FormStyles.buttonText}>
Search for List
</Text>
</TouchableOpacity>
</View>
}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<SeriesCard book={item} user={user.id = 0} />
)}
/>
}
{lists == undefined &&
<View style={FormStyles.card}>
<TextInput
style={FormStyles.input}
placeholder="List Name"
value={listNameSearch}
onChangeText={setListNameSearch}
/>
<TouchableOpacity
style={FormStyles.button}
onPress={handleSearch}
>
<Text style={FormStyles.buttonText}>
Search for List
</Text>
</TouchableOpacity>
</View>
}
</View>
);
}
I've tried using event.preventDefault()
but that didn't seem to do anything. I've also tried a useEffect
.
const [listNameSearch, setListNameSearch] = useState('');
const [search, setSearch] = useState(false);
const handleSearch = () => {
setSearch(true);
}
useEffect(() => {
if (search) {
refetchLists({ listNameSearch, page: 1 });
}
}, [search]);
That didn't avoid the continuous searches either. What's the secret?
useSearchListsQuery
is called each render cycle and passeslistNameSearch
as a query argument. This unconditionally runs the query endpoint. If you are only wanting to call the query endpoint when a specific event happens, e.g. the user presses a button in the UI, then use the lazy query hook. The lazy query hook returns a trigger function, the current result, and the last argument passed to the query.A refactor of the code may look like the following.
API slice