React输入文本变更时隐藏下拉菜单的技术求助
实现输入变更时立即隐藏下拉菜单的防抖搜索组件
要实现输入框字符变更时立即隐藏下拉菜单,直到防抖后的搜索结果返回,核心思路是对比实时输入值与已完成防抖处理的查询值,只有当两者一致时才展示下拉菜单。具体修改步骤如下:
1. 更新上下文类型与Provider
首先需要将防抖后的查询值appliedQuery添加到上下文,让子组件能获取到这个值进行判断:
修改PeopleContext.tsx:
import React, { createContext, useState, useCallback, useMemo, ReactNode, } from 'react'; import debounce from 'lodash/debounce'; import { Person } from '../types/Person'; import { peopleFromServer } from '../data/people'; // 1. 更新上下文类型,新增appliedQuery字段 interface PeopleContextType { input: string; setInput: (query: string) => void; applyQueryWithDebounce: (query: string) => void; handleClick: (person: string) => void; filteredPeople: Person[]; selectedPerson: string; appliedQuery: string; // 新增字段 } const PeopleContext = createContext<PeopleContextType>({} as PeopleContextType); export const PeopleProvider: React.FC<{ children: ReactNode }> = ({ children, }) => { const [people, setPeople] = useState<Person[] | null>(peopleFromServer); const [input, setInput] = useState<string>(''); const [selectedPerson, setSelectedPerson] = useState<string>('No selected person'); const [appliedQuery, setAppliedQuery] = useState<string>(''); const applyQueryWithDebounce = useCallback( debounce(setAppliedQuery, 1000), [], ); const filteredPeople = useMemo(() => { return peopleFromServer.filter((person) => person.name .toLowerCase() .includes(appliedQuery.toLowerCase())); }, [people, appliedQuery]); const handleClick = useCallback((person: string) => { setSelectedPerson(person); setPeople([]); setInput(''); }, []); return ( <PeopleContext.Provider value={{ input, setInput, applyQueryWithDebounce, handleClick, filteredPeople, selectedPerson, appliedQuery, // 2. 将appliedQuery加入上下文提供的值中 }} > {children} </PeopleContext.Provider> ); }; export default PeopleContext;
2. 修改下拉菜单的显示条件
在People组件中,获取appliedQuery,并修改下拉菜单的渲染条件:只有当实时输入input不为空,且与防抖后的appliedQuery完全一致时,才展示下拉菜单。
修改People.tsx:
import React, { useContext } from 'react'; import cn from 'classnames'; import PeopleContext from '../contexts/PeopleContext'; export const People = React.memo(() => { const { input, setInput, applyQueryWithDebounce, filteredPeople, handleClick, appliedQuery, // 1. 从上下文获取appliedQuery } = useContext(PeopleContext); const handleInput = (event: React.ChangeEvent<HTMLInputElement>) => { const userInput = event.target.value; applyQueryWithDebounce(userInput); setInput(userInput); }; return ( <div className="dropdown is-active"> <div className="dropdown-trigger"> <input type="search" placeholder="Enter the name" className="input" value={input} onChange={handleInput} /> </div> {/* 2. 修改显示条件:input不为空且与appliedQuery一致 */} {input && input === appliedQuery && ( <div className="dropdown-menu" role="menu"> <div className="dropdown-content"> {filteredPeople.length > 0 && filteredPeople.map((person) => ( <button key={person.slug} type="button" className="dropdown-item person-input-item" onClick={() => { handleClick( `${person.name} (${person.born} - ${person.died})`, ); }} > <p className={cn('has-text-link', { 'has-text-danger': person.sex === 'f', })} > {person.name} </p> </button> ))} {!filteredPeople.length && ( <p className="dropdown-item has-text-danger"> No matching suggestions </p> )} </div> </div> )} </div> ); };
改动说明
- 当用户输入字符时,
input会实时更新,而appliedQuery因为防抖延迟还未同步,此时input !== appliedQuery,下拉菜单隐藏; - 防抖时间结束后,
appliedQuery更新为当前input的值,两者相等,下拉菜单重新展示搜索结果; - 若用户连续输入,每次输入都会触发
input更新,但只有最后一次输入的防抖触发后,appliedQuery才会匹配当前input,确保下拉菜单只在搜索结果准备好后显示。
内容的提问来源于stack exchange,提问作者Yevhen Petrovskyi
相关产品推荐
相关产品推荐

