React诊所定位器输入框无法输入及输入值获取问题
问题分析与修复方案
1. 输入框无法输入的核心原因
你的handleChange函数定义错误,是嵌套函数结构,而React的onChange需要直接接收事件处理函数;同时每次输入都重复请求诊所列表的逻辑既低效又会干扰输入状态更新。
2. 具体修复步骤
修复handleChange函数
将嵌套函数改为直接接收事件参数的函数,移除内部不必要的getClinic调用:
const handleChange = (e) => { const value = e.target.value; setSearchClinic(value); filter(value); };
用useEffect一次性获取诊所列表
在组件挂载时调用getClinic,避免每次输入重复发起请求:
useEffect(() => { getClinic() .then((response) => { console.log(response); setClinicList(response); setClinicListFiltered(response); }) .catch((error) => { console.error(error); }); }, []); // 空依赖数组确保只在组件挂载时运行一次
优化filter函数
增加空值判断,避免空输入时的无效过滤,同时统一处理大小写:
const filter = (termSearch) => { if (!termSearch) { setClinicListFiltered(clinicList); return; } const lowerTerm = termSearch.toLowerCase(); const resultSearch = clinicList.filter((element) => { return ( (element.address?.toLowerCase().includes(lowerTerm)) || (element.province?.toLowerCase().includes(lowerTerm)) || (element.town?.toLowerCase().includes(lowerTerm)) ); }); setClinicListFiltered(resultSearch); };
3. 获取输入框的值
输入框的值已经绑定到searchClinic状态变量,可直接在组件任意位置使用该变量获取当前输入值。例如在搜索按钮的点击事件中:
const handleSearch = () => { console.log("当前输入位置:", searchClinic); // 此处可添加基于输入值的额外逻辑,比如地理定位、更精准的筛选等 closeModalLocator(); };
然后修改按钮的onClick属性:
<button className="btn bg-teal-600 hover:bg-teal-700 text-white text-sm active:bg-teal-700 px-8 outline-none focus:outline-none" type="button" onClick={handleSearch}>Buscar</button>
完整修复后的组件代码
import React, { useState, useEffect } from 'react' import { getClinic } from '../../api/drupalAPI' import { Clinic } from '@icofcv/common'; import Spinner from '../spinner/Spinner'; interface Props { showModalLocator: boolean, closeModalLocator: () => void } export const ClinicLocator: React.FC<Props> = ({ showModalLocator, closeModalLocator }) => { const [clinicList, setClinicList] = useState<Clinic[]>([]); const [clinicListFiltered, setClinicListFiltered] = useState<Clinic[]>([]); const [searchClinic, setSearchClinic] = useState(""); useEffect(() => { getClinic() .then((response) => { console.log(response); setClinicList(response); setClinicListFiltered(response); }) .catch((error) => { console.error(error); }); }, []); const handleChange = (e) => { const value = e.target.value; setSearchClinic(value); filter(value); }; const filter = (termSearch) => { if (!termSearch) { setClinicListFiltered(clinicList); return; } const lowerTerm = termSearch.toLowerCase(); const resultSearch = clinicList.filter((element) => { return ( (element.address?.toLowerCase().includes(lowerTerm)) || (element.province?.toLowerCase().includes(lowerTerm)) || (element.town?.toLowerCase().includes(lowerTerm)) ); }); setClinicListFiltered(resultSearch); }; const handleSearch = () => { console.log("当前输入位置:", searchClinic); // 可添加额外搜索逻辑 closeModalLocator(); }; return ( <> <div> {showModalLocator ? ( <> <div className="justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 z-50 outline-none focus:outline-none"> <div className="relative p-2 w-full max-w-3xl h-full md:h-auto"> <div className="relative bg-white rounded-lg shadow"> <div className="flex justify-between items-start px-4 py-3 rounded-t border-b"> <h3 className="text-lg font-medium">Localizador de clinicas</h3> <button className="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center" onClick={closeModalLocator}> <svg aria-hidden="true" className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path> </svg> </button> </div> <div className="relative px-3 py-3 flex-auto overflow-auto modal-body"> <h2 className="text-sm font-medium mb-2">¿Dónde te encuentras?</h2> <input value={searchClinic} onChange={handleChange} type="search" className="w-100 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2" placeholder="Introduce una ubicación" /> <div className="py-3 border-b flex flex-col md:flex-row items-start md:items-center md:justify-between justify-start gap-2"> <div className="flex items-center"> <label className="inline-flex relative items-center mr-5 cursor-pointer"> <input type="checkbox" className="sr-only peer" checked /> <div className="w-11 h-6 bg-gray-200 rounded-full peer peer-focus:ring-teeal-600 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-teal-600"></div> <span className="ml-2 text-xs font-medium text-gray-900">Centro privado</span> </label> </div> <div className="flex items-center"> <label className="inline-flex relative items-center mr-5 cursor-pointer"> <input type="checkbox" className="sr-only peer" readOnly /> <div className="w-11 h-6 bg-gray-200 rounded-full peer peer-focus:ring-teeal-600 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-teal-600"></div> <span className="ml-2 text-xs font-medium text-gray-900">con Mútuas</span> </label> </div> <div className="flex items-center"> <label className="inline-flex relative items-center mr-5 cursor-pointer"> <input type="checkbox" className="sr-only peer" readOnly /> <div className="w-11 h-6 bg-gray-200 rounded-full peer peer-focus:ring-teeal-600 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-teal-600"></div> <span className="ml-2 text-xs font-medium text-gray-900">con Compañías de Salud</span> </label> </div> </div> <div> <h2 className="text-sm font-medium my-3">Resultados</h2> <div className="w-100"> <iframe className="w-100" src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2664.3238269926374!2d-0.3805919350162851!3d39.46959682083709!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0xd604f4bee0957f3%3A0x6686ff7d230b3965!2zQy4gZGUgU2FudC BWaWNlbnQgTcOgcnRpciwgNjEsIHBpc28gMsK6LCBwdGEgMsKqLCA0NjAwMiBWYWzDqG5jaWEsIEVzcGHDsWE!5e0!3m2!1ses!2sus!4v1662388390673!5m2!1ses!2sus" loading="lazy"></iframe> </div> <div className="md:mt-4 overflow-auto relative py-2"> <div className="grid grid-cols-1 md:grid-cols-2 gap-2"> {clinicListFiltered.length === 0 ? ( <Spinner /> ) : ( clinicListFiltered.map((clinic) => ( <div key={clinic.id} className="card bg-white px-2 py-3 h-36 md:h-32"> <button type="button" className="text-left"> <div className="flex items-center gap-2 md:gap-4"> <img className="h-24 w-2/5 min-w-40 object-cover object-center rounded-lg" src="../carousel-1.svg" alt="#"/> <div className="w-3/5"> <div className="text-md font-medium leading-5 clinic-title uppercase">{clinic.title}</div> <div className="flex items-center gap-2"> <div className="text-neutral-500 text-sm">{clinic.propsPhone}</div> <div className="text-neutral-500 text-sm">{clinic.mobile}</div> </div> <div className="text-teal-600 text-sm underline clinic-mail">{clinic.email}</div> <div className="text-neutral-500 text-sm">{clinic.registry}</div> </div> </div> </button> </div> )) )} </div> </div> </div> </div> <div className="flex items-center justify-end px-4 py-2 border-t border-solid border-slate-200 rounded-b gap-2"> <button className="btn text-black text-sm background-transparent px-8 outline-none focus:outline-none focus:ring-teal-600 focus:border-teal-600" type="button" onClick={closeModalLocator}>Cancelar</button> <button className="btn bg-teal-600 hover:bg-teal-700 text-white text-sm active:bg-teal-700 px-8 outline-none focus:outline-none" type="button" onClick={handleSearch}>Buscar</button> </div> </div> </div> </div> <div className="opacity-25 fixed inset-0 z-40 bg-black"></div> </> ) : null} </div> </> ) }
额外优化说明
- 修复了Spinner的判断逻辑,原代码
!clinicListFiltered会将空数组误判为加载状态,改为判断数组长度更准确 - 将诊所列表的map渲染移到grid容器下,避免所有诊所项都挤在同一个card里
- 新增
handleSearch函数统一处理搜索按钮逻辑,便于后续扩展功能
内容的提问来源于stack exchange,提问作者cuencadelrio
相关产品推荐
相关产品推荐

