React Native Web基于Context与API的动物列表过滤功能失效排查
React Native Web 动物列表过滤功能问题排查与修复
问题描述
我有一个React Native Web应用,需实现动物列表过滤功能,过滤数据由API调用生成,并通过Context注入API调用逻辑。在SubCategoryScreen组件中调用Context内的performSearch方法时,搜索“Wolf”等关键词后FlatList无变化。DevTools显示API已返回正确结果(如包含Wolf的数组),但直接在组件内编写API调用逻辑时功能正常;同时组件内输入清空判断逻辑仅输出TEXT日志,无对应处理。现需解决如何让FlatList正确显示过滤后的动物数据。
相关代码
API调用代码
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; } };
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> ); };
SubCategoryScreen组件代码
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> ); };
问题根源与修复方案
核心问题分析
- 状态同步失效:组件维护了本地
results状态,但未订阅Context中的results变化,导致API返回的正确数据无法传递到FlatList。 - 定时器调用错误:
setTimeout(performSearch(text), 1000)会立即执行performSearch,而非延迟1秒执行,触发时机错误。 - 输入清空无处理:输入框清空时仅打印日志,未重置搜索状态,FlatList无法回到初始列表。
- Context状态逻辑混乱:
searchAnimal初始值为数组,但实际用于存储搜索文本,导致filteredData计算错误。
修复后的代码
修正Context逻辑
import React, { createContext, useState } from "react"; import { fetchAnimalData } from "./animal/animal.service"; export const SearchAnimalContext = createContext(); export const SearchAnimalContextProvider = ({ children }) => { // 修正:searchAnimal存储搜索文本,初始值改为字符串 const [searchAnimal, setSearchAnimal] = useState(""); const [results, setResults] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const handleSearch = (text) => { setSearchAnimal(text); }; // 移除无用的filteredData(组件内有本地过滤逻辑) const performSearch = async (text) => { setLoading(true); setError(null); try { const response2 = await fetchAnimalData(text); setResults(response2); } catch (err) { setError(err); } finally { setLoading(false); } }; return ( <SearchAnimalContext.Provider value={{ searchAnimal, setSearchAnimal, handleSearch, performSearch, results, loading, error, }}> {children} </SearchAnimalContext.Provider> ); };
修正SubCategoryScreen组件
export const SubCategoryScreen = ({ route, navigation }) => { const [subCatgoryList, setSubCategoryList] = useState([]); const [isLoading, setLoading] = useState(true); const [searchAnimal, setSearchAnimal] = useState(""); const [input, setInput] = useState(""); // 订阅Context中的results和loading状态 const { performSearch, results, loading: contextLoading } = useContext(SearchAnimalContext); const [searchTimer, setSearchTimer] = useState(null); 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()) ); // 重构搜索输入处理逻辑 const handleSearchInput = (text) => { if (searchTimer) clearTimeout(searchTimer); setInput(text); // 输入清空时重置状态 if (text.length === 0) { setSearchAnimal(""); return; } // 修正定时器:传递函数而非立即执行 setSearchTimer(setTimeout(() => { performSearch(text); setSearchAnimal(text); }, 1000)); }; return ( <SafeArea> {/* 合并加载状态 */} {(isLoading || contextLoading) && ( <LoadingContainer> <ActivityIndicator animating={true} color={MD2Colors.green200} /> </LoadingContainer> )} <Searchbar placeholder="Searchie" onChangeText={handleSearchInput} value={input} /> {/* 使用Context的results而非本地状态 */} {results.length > 0 ? ( <CategoryList data={results} renderItem={({ item }) => ( <Spacer> <SubCategoryInfoCard subcategories={item} /> </Spacer> )} keyExtractor={(item) => item.id} /> ) : ( <CategoryList data={filteredData} renderItem={({ item }) => ( <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> ); };
关键修复说明
- 状态同步:组件直接使用Context中的
results,确保API返回数据实时更新到FlatList。 - 定时器修正:将
setTimeout(performSearch(text), 1000)改为setTimeout(() => performSearch(text), 1000),实现延迟搜索。 - 输入清空处理:输入框清空时重置搜索文本,让FlatList回到初始列表状态。
- Context逻辑简化:修正
searchAnimal初始值类型,移除无用计算,避免逻辑冲突。
内容的提问来源于stack exchange,提问作者mightycode Newton
相关产品推荐
相关产品推荐

