React输入框过滤API获取数据异常问题排查与修复
问题:输入关键词后API数据过滤未达预期
我设置了一个输入框,期望根据标题过滤从API获取的数据,但输入正确关键词后,过滤结果并未达到预期效果。
代码示例
"use client"; import React, { useEffect, useState } from 'react'; const FetchWithFilter = () => { const [data, setData] = useState([]); const [filter, setFilter] = useState(""); const [filteredData, setFilteredData] = useState([]); async function fetchData() { try { const response = await fetch('https://jsonplaceholder.typicode.com/posts'); const data = await response.json(); setData(data); setFilteredData(data); // initialize filteredData } catch (error) { console.error("Error fetching data:", error); } } const handleFilterChange = (event: React.ChangeEvent<HTMLInputElement>) => { const value = event.target.value; setFilter(value); const filtered = data.filter(item => item.title.toLowerCase().includes(value.toLowerCase()) ); setFilteredData(filtered); }; useEffect(() => { fetchData(); }, []); return ( <div> <h1 className="text-2xl font-bold">Fetch Data</h1> <input type="text" placeholder="Filter by title..." value={filter} onChange={handleFilterChange} className="border border-gray-300 rounded px-2 py-1 mb-4" /> <button onClick={fetchData} className="bg-[red] rounded-2xl px-4"> Fetch Data </button> <ul className="space-y-2"> {filteredData.map(item => ( <li className="border-b pb-2" key={item.id}> <strong>{item.title}</strong> <p>{item.body}</p> </li> ))} </ul> </div> ); }; export default FetchWithFilter;
问题原因
- 状态不同步:每次调用
fetchData时,会直接将filteredData重置为全部数据,忽略当前的filter值。比如输入关键词过滤出结果后,点击Fetch Data按钮,过滤结果会被重置为所有数据。 - 闭包捕获旧状态:数据加载完成前输入关键词,过滤会基于空数组返回空结果;数据加载完成后,
filteredData被设为全部数据,但此时filter仍保留输入值,却未重新执行过滤,导致显示全部数据而非预期结果。 - 冗余状态维护:
filteredData属于衍生状态,可通过data和filter实时计算,单独维护容易出现状态不同步问题。
修复方法
方案一:用useEffect监听状态变化更新过滤结果
将过滤逻辑移到useEffect中,只要data或filter变化就重新计算:
"use client"; import React, { useEffect, useState } from 'react'; const FetchWithFilter = () => { const [data, setData] = useState([]); const [filter, setFilter] = useState(""); const [filteredData, setFilteredData] = useState([]); async function fetchData() { try { const response = await fetch('https://jsonplaceholder.typicode.com/posts'); const fetchedData = await response.json(); setData(fetchedData); } catch (error) { console.error("Error fetching data:", error); } } // 监听data和filter变化,重新计算过滤结果 useEffect(() => { const filtered = data.filter(item => item.title.toLowerCase().includes(filter.toLowerCase()) ); setFilteredData(filtered); }, [data, filter]); const handleFilterChange = (event: React.ChangeEvent<HTMLInputElement>) => { setFilter(event.target.value); }; useEffect(() => { fetchData(); }, []); return ( <div> <h1 className="text-2xl font-bold">Fetch Data</h1> <input type="text" placeholder="Filter by title..." value={filter} onChange={handleFilterChange} className="border border-gray-300 rounded px-2 py-1 mb-4" /> <button onClick={fetchData} className="bg-[red] rounded-2xl px-4"> Fetch Data </button> <ul className="space-y-2"> {filteredData.map(item => ( <li className="border-b pb-2" key={item.id}> <strong>{item.title}</strong> <p>{item.body}</p> </li> ))} </ul> </div> ); }; export default FetchWithFilter;
方案二:移除冗余状态,渲染时实时计算(推荐)
直接在渲染阶段计算过滤结果,避免维护额外状态:
"use client"; import React, { useEffect, useState } from 'react'; const FetchWithFilter = () => { const [data, setData] = useState([]); const [filter, setFilter] = useState(""); async function fetchData() { try { const response = await fetch('https://jsonplaceholder.typicode.com/posts'); const fetchedData = await response.json(); setData(fetchedData); } catch (error) { console.error("Error fetching data:", error); } } const handleFilterChange = (event: React.ChangeEvent<HTMLInputElement>) => { setFilter(event.target.value); }; useEffect(() => { fetchData(); }, []); // 实时计算过滤结果 const filteredData = data.filter(item => item.title.toLowerCase().includes(filter.toLowerCase()) ); return ( <div> <h1 className="text-2xl font-bold">Fetch Data</h1> <input type="text" placeholder="Filter by title..." value={filter} onChange={handleFilterChange} className="border border-gray-300 rounded px-2 py-1 mb-4" /> <button onClick={fetchData} className="bg-[red] rounded-2xl px-4"> Fetch Data </button> <ul className="space-y-2"> {filteredData.map(item => ( <li className="border-b pb-2" key={item.id}> <strong>{item.title}</strong> <p>{item.body}</p> </li> ))} </ul> </div> ); }; export default FetchWithFilter;
说明
方案二更简洁可靠,彻底避免了状态不同步的问题。当data或filter变化时,组件会自动重新渲染并计算最新的过滤结果,确保显示内容始终符合预期。
内容的提问来源于stack exchange,提问作者FAIZAN AHMED TANOLI
相关产品推荐
相关产品推荐

