You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>
    );
};

问题根源与修复方案

核心问题分析

  1. 状态同步失效:组件维护了本地results状态,但未订阅Context中的results变化,导致API返回的正确数据无法传递到FlatList。
  2. 定时器调用错误:setTimeout(performSearch(text), 1000)会立即执行performSearch,而非延迟1秒执行,触发时机错误。
  3. 输入清空无处理:输入框清空时仅打印日志,未重置搜索状态,FlatList无法回到初始列表。
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 18:10:56