ReactJS中Home父组件与Search子组件搜索功能问题求助
问题解决思路与代码修改
问题1:搜索仅能生效一次的原因及解决
你的Home组件用同一个allData存储原始全量数据和过滤后的结果,第一次搜索后allData被替换为分类过滤后的子集,后续搜索失去了原始数据基础,导致无法正确过滤。
修改方案:拆分数据状态,保留原始全量数据,用单独的状态存储过滤结果。
问题2:距离参数未生效的原因及解决
Home组件未定义distance状态,handleSearch里的setDistance是未声明的函数,且搜索逻辑只监听categoriesSelected变化,完全忽略距离参数。
修改方案:在Home中添加distance状态,让搜索逻辑同时响应分类和距离的变化,同时确保参数正确传递。
修改后的Home组件代码
const Home = () => { const [rawData, setRawData] = useState([]); // 存储原始全量物品数据 const [filteredData, setFilteredData] = useState([]); // 存储最终展示的过滤后数据 const [categoriesSelected, setCategoriesSelected] = useState(""); const [distance, setDistance] = useState(1000); // 新增距离状态 const navigate = useNavigate(); const location = useLocation(); // 初始化获取全量数据 useEffect(() => { fetch("URL/api/item-list/") .then((res) => res.json()) .then((data) => { setRawData(data); setFilteredData(data); // 默认展示全部数据 }) .catch((e) => console.log("ERROR", e)); }, []); // 当分类或距离变化时,执行搜索逻辑 useEffect(() => { if (!rawData.length) return; // 原始数据未加载时不执行 // 推荐:调用后端合并接口(同时支持分类和距离过滤),减少前端请求次数 fetch( `URL/api/item-combined-filter/?categories=${categoriesSelected}&distance=${distance}&lat=${/* 需传入用户纬度,可从Search组件传递 */}&lng=${/* 需传入用户经度,可从Search组件传递 */}` ) .then((res) => res.json()) .then((data) => { setFilteredData(data); }) .catch((e) => console.log("ERROR", e)); // 备选:沿用你之前的前端过滤逻辑(如果后端无法修改接口) // 需要把Search中过滤好的结果直接传递过来,此时handleSearch改为接收filteredPosts // const handleSearch = (filteredPosts) => { // setFilteredData(filteredPosts); // }; }, [categoriesSelected, distance, rawData]); const handleSearch = (selectedCategories, selectedDistance) => { setCategoriesSelected(selectedCategories); setDistance(selectedDistance); }; return ( <div> <Search onSearch={handleSearch} /> <div> {filteredData.map((post) => { // 这里写你的数据展示逻辑,记得加key return <div key={post.id}>{post.name}</div>; })} </div> </div> ); }; export default Home;
修改后的Search组件代码
const Search = ({ onSearch }) => { const [categoriesSelected, setCategoriesSelected] = useState([]); const [categories, setCategories] = useState([]); const [lat, setLat] = useState(""); const [lng, setLng] = useState(""); const [distance, setDistance] = useState(1000); const [open, setOpen] = useState(false); useEffect(() => { fetch("URL/api/get-categories/") .then((res) => res.json()) .then((data) => setCategories(data)) .catch((e) => console.log("ERROR", e)); }, []); async function submitSearch(event) { event.preventDefault(); try { // 校验位置信息是否存在 if (!lat || !lng) { console.error("请先获取位置信息"); return; } // 如果用后端合并接口,这里可以直接调用合并接口,把结果传给Home // const response = await fetch( // `URL/api/item-combined-filter/?categories=${categoriesSelected.join(',')}&distance=${distance}&lat=${lat}&lng=${lng}` // ); // const filteredPosts = await response.json(); // onSearch(filteredPosts); // 沿用你原来的双接口过滤逻辑 const response1 = await fetch( `https://URL/api/item-categories-list/?categories=${categoriesSelected.join(',')}` // 多选数组转逗号分隔字符串 ); const response2 = await fetch( `https://URL/api/item-list-distance/?distance=${distance}&lat=${lat}&lng=${lng}` ); const data1 = await response1.json(); const data2 = await response2.json(); const filteredPosts = data1.filter((post1) => data2.some((post2) => post1.id === post2.id) ); // 方案1:传递过滤后的结果给Home(无需Home再处理) // onSearch(filteredPosts); // 方案2:传递分类和距离参数给Home,让Home自己请求接口 onSearch(categoriesSelected.join(','), distance); } catch (error) { console.error(error); } } return ( <div> <Card> {/* 你的搜索栏内容 */} </Card> <Card> <div> <Box> <FormControl onSubmit={submitSearch}> {/* 分类多选框实现(示例) */} {categories.map((category) => ( <div key={category.id}> <input type="checkbox" checked={categoriesSelected.includes(category.id)} onChange={(e) => { e.target.checked ? setCategoriesSelected([...categoriesSelected, category.id]) : setCategoriesSelected(categoriesSelected.filter(id => id !== category.id)); }} /> <label>{category.name}</label> </div> ))} {/* 位置组件 */} <Location setLat={setLat} setLng={setLng} lat={lat} lng={lng} /> {/* 距离选择下拉框(示例) */} <select value={distance} onChange={(e) => setDistance(Number(e.target.value))} > <option value={500}>500米</option> <option value={1000}>1公里</option> <option value={2000}>2公里</option> </select> <Button type="submit" onClick={submitSearch}> Search </Button> </FormControl> </Box> </div> </Card> </div> ); }; export default Search;
关键修改点总结
- 拆分数据状态:用
rawData保留原始全量数据,避免过滤后丢失数据源。 - 添加距离状态:在Home中声明
distance状态,确保能接收并使用Search传递的距离参数。 - 监听多参数变化:让搜索逻辑的
useEffect同时依赖categoriesSelected和distance,确保两个参数变化时都能触发搜索。 - 参数格式处理:分类多选数组转成逗号分隔的字符串,符合后端接口的参数接收格式。
- 位置校验:在Search的提交逻辑中校验经纬度是否存在,避免无效的距离过滤请求。
内容的提问来源于stack exchange,提问作者Jo_K
相关产品推荐
相关产品推荐

