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

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;

关键修改点总结

  1. 拆分数据状态:用rawData保留原始全量数据,避免过滤后丢失数据源。
  2. 添加距离状态:在Home中声明distance状态,确保能接收并使用Search传递的距离参数。
  3. 监听多参数变化:让搜索逻辑的useEffect同时依赖categoriesSelected和distance,确保两个参数变化时都能触发搜索。
  4. 参数格式处理:分类多选数组转成逗号分隔的字符串,符合后端接口的参数接收格式。
  5. 位置校验:在Search的提交逻辑中校验经纬度是否存在,避免无效的距离过滤请求。

内容的提问来源于stack exchange,提问作者Jo_K

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 20:18:11