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

React useMemo联动筛选列表回车选中后结果消失问题咨询

选型结论

这个场景不需要使用useEffect。所有列表筛选、预选首项的逻辑都是基于已有state计算得到的派生值,完全适合用useMemo实现;如果列表数据量很小,甚至可以省略useMemo直接在渲染函数中计算,性能差异可以忽略。
useEffect的适用场景是处理和渲染无关的副作用,比如接口请求、原生DOM操作、事件订阅/解绑,不要用它来派生计算状态,否则很容易出现状态同步滞后、重复渲染的问题。

问题根因

按下回车后列表消失和useMemo联动逻辑无关,是两个代码bug导致的:

  • 筛选逻辑大小写处理不一致:你只对国家名做了小写转换,没有对输入的搜索关键词做同样的小写转换。当你按下回车把搜索值设置为首字母大写的完整国家名时,类似"germany".includes("Germany")的判断会直接返回false,最终筛选结果为空数组,列表自然没有内容可渲染。
  • 下拉框显隐逻辑混乱:你声明了showSuggestionBox状态但没有实际使用,把显隐判断耦合在输入框聚焦状态和搜索词长度上,后续扩展交互很容易出问题。
修复方案
  1. 统一筛选逻辑的大小写处理,对搜索关键词也做小写转换,同时增加空值trim判断避免输入空格出现无意义匹配
  2. 理顺下拉框显隐逻辑,统一用showSuggestionBox控制下拉显示
  3. 修复后完整代码如下:
import React, { useState, useMemo } from "react";
const Test6 = () => {
  const [countries] = useState([
    { name: "Pakistan" },
    { name: "India" },
    { name: "Germany" },
    { name: "England" },
  ]);

  const [search, setSearch] = useState("");
  const [showSuggestionBox, setShowSuggestionBox] = useState(false);
  // 筛选逻辑:统一转小写匹配
  const filteredCountries = useMemo(() => {
    if (!search.trim()) return countries;
    const keyword = search.trim().toLowerCase();
    return countries.filter((country) =>
      country.name.toLowerCase().includes(keyword)
    );
  }, [search, countries]);

  // 预选首项逻辑
  const selectedCountry = useMemo(() => {
    if (!search.trim() || !showSuggestionBox) return null;
    return filteredCountries[0] ?? null;
  }, [filteredCountries, search, showSuggestionBox]);

  return (
    <div>
      <div>
        <input
          onKeyUp={(e) => {
            if (e.key === "Enter" && selectedCountry) {
              setSearch(selectedCountry.name);
              // 若需要选中后收起下拉,打开下面这行注释即可
              // setShowSuggestionBox(false);
            }
          }}
          type="text"
          onBlur={() => {
            // 加延迟避免后续加点击下拉项功能时,blur提前触发导致点击失效
            setTimeout(() => setShowSuggestionBox(false), 100);
          }}
          onFocus={() => {
            setShowSuggestionBox(true);
          }}
          value={search}
          placeholder="Search Country"
          onChange={(e) => {
            setSearch(e.target.value);
            setShowSuggestionBox(true);
          }}
        />
        {/* 统一用showSuggestionBox和筛选结果长度控制下拉显示 */}
        {showSuggestionBox && filteredCountries.length > 0 ? (
          <div className="">
            {filteredCountries.map((country) => {
              return (
                <div
                  key={country.name}
                  className={`${
                    search.trim() ? "first:bg-blue-500 first:text-white" : ""
                  }`}>
                  {country.name}
                </div>
              );
            })}
          </div>
        ) : null}
      </div>
    </div>
  );
};

export default Test6;
补充说明

两个useMemo的联动逻辑本身没有问题,filteredCountries作为selectedCountry的依赖是完全符合React数据流设计的,只要依赖数组填写正确,不会出现更新顺序问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 06:39:26