I have a react native web app. And I try to create a filter function for animals. The data that will return via the filter function is generated by an api call. And I am using a context for injecting the api call. And then in the component I try to call the filter function from the context in the component.
And I am using flatlist for showing the animals on the cards. But for example if I try to filter the list with Wolf. Nothing happens.
So this is the api call:
export const fetchAnimalData = async (text) => {
const token = await retrieveToken();
try {
if (token) {
const response = await fetch(`${API_URL}/api/animals/?name=${text}`, {
method: "GET",
headers: {
Authorization: `Token ${token}`,
Accept: "application/json",
"Content-Type": "application/json",
},
});
return await response.json();
} else {
throw new Error(token);
}
} catch (error) {
console.error("There was a problem with the fetch operation:", error);
throw error;
}
};
And this is the context:
import React, { createContext, useEffect, useState } from "react";
import { fetchAnimalData } from "./animal/animal.service";
export const SearchAnimalContext = createContext();
export const SearchAnimalContextProvider = ({ children }) => {
const [searchAnimal, setSearchAnimal] = useState([]);
const [results, setResults] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const handleSearch = (text) => {
setSearchAnimal(text);
};
const filteredData = searchAnimal.filter((item) =>
item.name.toLowerCase().includes(searchAnimal.toLowerCase())
);
const performSearch = async (text) => {
setLoading(true);
setError(null);
fetchAnimalData(text)
.then((response2) => {
setResults(response2);
setIsLoading(false);
})
.catch((err) => {
setLoading(false);
setError(err);
});
};
return (
<SearchAnimalContext.Provider
value={{
filteredData,
searchAnimal,
setSearchAnimal,
handleSearch,
performSearch,
results,
loading,
error,
}}>
{children}
</SearchAnimalContext.Provider>
);
};
And I inject the function performSearch from the context in this component:
export const SubCategoryScreen = ({ route, navigation }) => {
const [subCatgoryList, setSubCategoryList] = useState([]);
const [isLoading, setLoading] = useState(true);
const [searchAnimal, setSearchAnimal] = useState("");
const [input, setInput] = useState("");
const { performSearch } = useContext(SearchAnimalContext);
const [searchTimer, setSearchTimer] = useState(null);
const [results, setResults] = useState([]);
const { toggle, showAccordion } = useContext(ToggleContext);
const [toggleIcon, setToggleIcon] = useState(true);
const handleClickIcon = () => {
setToggleIcon(!toggleIcon);
};
const filteredData = subCatgoryList.filter((item) =>
item.name.toLowerCase().includes(searchAnimal.toLowerCase())
);
return (
<SafeArea>
{isLoading && (
<LoadingContainer>
<ActivityIndicator animating={true} color={MD2Colors.green200} />
</LoadingContainer>
)}
<Searchbar
placeholder="Searchie"
onChangeText={(text) => {
if (searchTimer) {
clearTimeout(searchTimer);
}
if (text.length === 0) {
console.log(text, "TEXT");
return null;
}
setInput(text);
setSearchTimer(setTimeout(performSearch(text), 1000));
}}
value={input}
/>
{results.length > 0 ? (
<CategoryList
data={results}
renderItem={({ item }) => {
return (
<>
<Spacer>
<SubCategoryInfoCard subcategories={item} />
</Spacer>
</>
);
}}
keyExtractor={(item) => item.id}
/>
) : (
<CategoryList
data={filteredData}
renderItem={({ item }) => {
return (
<>
<TouchableOpacity
onPress={() => navigation.navigate("groepen", { subcategories: item.id })}
disabled={
isLoading ||
(!item.hasOwnProperty("animals") && !item.hasOwnProperty("subcategories"))
}>
<Spacer>
<SubCategoryInfoCard subcategories={item} />
</Spacer>
</TouchableOpacity>
</>
);
}}
keyExtractor={(item) => item.id}
/>
)}
</SafeArea>
);
};
But in the list nothing is filtered. But if I put a breakpoint on this line in the context component: setResults(response2);
in devtools
Then It reruns the correct result. For example I search for Wolf
response2: Array(1)
0: {id: 8, name: 'Wolf', sort: 'Canis lupus', uis: false, cites: 'nvt', …}
length: 1
[[Prototype]]: Array(0)
If I put the api call method directly in the SubCategoryScreen
component with this in it:
response.json().then((response2) => {
setResults(response2);
Then it works
But if I do this in the SubCategoryScreen :
if (text.length === 0) {
console.log(text, "TEXT");
return null;
}
Then I only see the TEXT message. But not the result of text
Question: how to filter a specific animal from the flatlist?
In SubCategoryScreen
you use:
const [results, setResults] = useState([]);
Which is never updated. performSearch
will update the results
in the context, but not in the results
state in the SubCategoryScreen
component
You probably want to use the following instead:
const { performSearch, results } = useContext(SearchAnimalContext);
Similarly you might also want to pull loading
/error
from the context.