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

React搜索列表点击外部关闭及跳转后不消失问题求助

React搜索栏点击外部关闭与跳转后列表残留问题解决方案

问题1:点击搜索结果时列表也关闭

原因:你的searchRef仅绑定在输入框上,搜索列表不在该ref的范围内,导致点击列表时被判定为外部点击,触发setInput("")让列表消失。

解决方法:将ref绑定到包含搜索框和列表的容器(FormContainer)上,确保列表属于内部元素;同时给监听事件的useEffect添加空依赖数组,避免重复绑定事件。

问题2:跳转页面后再次搜索点击,列表不消失

原因:路由跳转后SearchBar组件未卸载,input状态保留,导致列表持续显示。

解决方法:在点击列表项跳转时,手动调用父组件方法清空input;或监听路由变化自动清空输入框。


修改后的SearchBar组件

import styled from "styled-components";
import { HiOutlineSearch } from "react-icons/hi";
import { useEffect, useRef, useState } from "react";
import { useArticle } from "../contexts/ArticleProvider";
import SearchList from "./SearchList";

const FormContainer = styled.div`
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
`;

const Form = styled.form`
  display: flex;
  align-items: center;
  background-color: #fff;
  border: 1px solid #ccc;
  border-radius: 4px;
  overflow: hidden;
  max-width: 600px;
  width: 100%;
`;

const Input = styled.input`
  flex: 1;
  border: none;
  padding: 8px 12px;
  font-size: 16px;
  &:focus {
    outline: none;
  }
`;
const Button = styled.button`
  background-color: #fff;
  border: none;
  padding: 8px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;

  &:hover {
    background-color: #f0f0f0;
  }

  svg {
    width: 20px;
    height: 20px;
  }
`;

function SearchBar() {
  const [input, setInput] = useState("");
  const [filteredArticles, setFilteredArticles] = useState([]);
  const { data } = useArticle();
  // 将ref绑定到包含搜索框和列表的容器
  const searchRef = useRef();

  useEffect(() => {
    if (input) {
      const results = data.filter(
        (article) =>
          article.title.toLowerCase().includes(input.toLowerCase()) ||
          article.category.toLowerCase().includes(input.toLowerCase()) ||
          article.product.toLowerCase().includes(input.toLowerCase())
      );
      setFilteredArticles(results.slice(0, 5));
    } else {
      setFilteredArticles(data);
    }
  }, [input, data]);

  function handleChange(value) {
    setInput(value);
  }

  // 点击外部关闭逻辑,添加空依赖数组避免重复绑定
  useEffect(() => {
    const handler = (e) => {
      if (searchRef.current && !searchRef.current.contains(e.target)) {
        setInput("");
      }
    };

    document.addEventListener("mousedown", handler);

    return () => {
      document.removeEventListener("mousedown", handler);
    };
  }, []);

  // 传给子组件的清空输入框方法
  const handleClearInput = () => {
    setInput("");
  };

  return (
    <FormContainer ref={searchRef}>
      <Form>
        <Input
          placeholder="Ürün, kategori ya da marka arayın"
          value={input}
          onChange={(e) => handleChange(e.target.value)}
        />
        <Button>
          <HiOutlineSearch />
        </Button>
      </Form>
      {input && <SearchList articles={filteredArticles} onItemClick={handleClearInput} />}
    </FormContainer>
  );
}

export default SearchBar;

修改后的SearchList组件

import { useNavigate } from "react-router-dom";
import styled from "styled-components";

const ArticlesList = styled.div`
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 1000;
  background-color: #fff;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  max-width: 600px;
  margin: 0 auto;
`;

const ArticleItem = styled.div`
  padding: 15px;
  border-bottom: 1px solid #eee;
  transition: background-color 0.3s ease;

  &:hover {
    background-color: #f9f9f9;
    cursor: pointer;
  }

  h3 {
    margin: 0 0 10px 0;
    font-size: 18px;
    color: #333;
  }

  p {
    margin: 5px 0;
    color: #666;
  }
`;

function ArticleList({ articles, onItemClick }) {
  const navigate = useNavigate();

  function handleNavigate(id) {
    navigate(`/makale/${id}`);
    // 跳转后调用父组件方法清空输入框
    onItemClick();
  }

  return (
    <ArticlesList>
      {articles.map((article) => (
        <ArticleItem
          key={article.id}
          onClick={() => handleNavigate(article.id)}
        >
          <p>{article.product}</p>
        </ArticleItem>
      ))}
    </ArticlesList>
  );
}

export default ArticleList;

备选优化:监听路由变化自动清空

如果SearchBar挂载在全局布局中(路由跳转后不会卸载),可以添加路由监听确保页面切换时输入框清空:

import { useLocation } from "react-router-dom";

// 在SearchBar组件中添加
const location = useLocation();
useEffect(() => {
  setInput("");
}, [location.pathname]);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 12:34:58