React天气应用中如何让OnChange与OnKeyDown事件协同工作?
问题
开发对接OpenWeather API的天气应用,需求如下:
- 搜索输入框在用户按下回车键后获取输入值
- 保留
onChange与onKeyDown事件
当前遇到的问题:
- 用
onChange获取值,onKeyDown调用searchLocation更新URL参数传位置给API,但按下回车后位置不更新 - 移除部分逻辑会导致API被频繁调用
- 尝试用全局布尔变量控制无效
- 需要从更新后的URL中获取用户输入的位置信息
.tsx核心逻辑代码
const searchParams = useSearchParams(); const pathName = usePathname(); const { replace } = useRouter(); globalThis.keyPressed = false let location = ''; function setLocation(lct: string, evt: ChangeEvent<HTMLInputElement>){ evt.preventDefault(); if(globalThis.keyPressed){ const selectedLocation = searchParams.get("location"); replace(`?${new URLSearchParams({location: lct})}`) ApiHandler(location); } } function searchLocation(evt: React.KeyboardEvent<HTMLInputElement>){ evt.preventDefault(); globalThis.keyPressed = true; const params = new URLSearchParams(searchParams); if (location != ''){ params.set('query', location); } else{ params.delete('query'); } replace(`${pathName}?${params.toString()}`); } // 目标输入框 <input value={typeof location === 'string' ? location : ''} onChange={event => {setLocation(event.target.value, event)}} onKeyDown={ event => {event.key === 'Enter' ? searchLocation(event) : null} } placeholder="Enter Location" type="text" defaultValue={searchParams.get('query')?.toString()} />
.jsx接口处理代码
export default async function ApiHandler(location) { try{ const data = await fetch( `https://api.openweathermap.org/data/2.5/weather?q=${location}&units=imperial&appid=${process.env.API_KEY}` ); const res = await data.json(); console.log(res.data); } catch (error){ console.log("Error connecting to api"); return new Response("Error in getting weather data", { status: 500 }); } }
.d.ts全局变量声明
declare global { var keyPressed: boolean }
解决方案
问题根源
- 用
let location = ''声明的普通变量非响应式,React无法追踪其变化,导致值更新后UI与逻辑不同步 - 全局变量
keyPressed的控制逻辑混乱,onChange触发时keyPressed尚未被设为true,条件判断失效 - 输入框同时设置
value和defaultValue引发冲突,非响应式变量无法正确同步输入值
修复步骤
- 改用
useState管理输入值,确保响应式更新 - 去掉全局变量,直接在回车事件中处理URL更新与API调用
- 统一用状态值绑定输入框
value,从URL参数初始化状态
修复后的代码
.tsx核心逻辑
import { useState, useEffect } from 'react'; import { useSearchParams, usePathname, useRouter } from 'next/navigation'; import ApiHandler from './ApiHandler'; export default function SearchInput() { const searchParams = useSearchParams(); const pathName = usePathname(); const { replace } = useRouter(); // 用useState管理输入值,从URL参数初始化 const [location, setLocation] = useState(searchParams.get('query') || ''); // 监听URL参数变化,同步到输入框(页面刷新或其他地方修改URL时) useEffect(() => { const query = searchParams.get('query'); if (query !== location) { setLocation(query || ''); } }, [searchParams, location]); // 回车事件处理:更新URL + 调用API const handleKeyDown = (evt: React.KeyboardEvent<HTMLInputElement>) => { if (evt.key === 'Enter') { evt.preventDefault(); const trimmedLocation = location.trim(); if (!trimmedLocation) return; // 更新URL参数 const params = new URLSearchParams(searchParams); params.set('query', trimmedLocation); replace(`${pathName}?${params.toString()}`); // 调用API ApiHandler(trimmedLocation); } }; // 输入变化仅更新状态 const handleChange = (evt: React.ChangeEvent<HTMLInputElement>) => { setLocation(evt.target.value); }; return ( <input value={location} onChange={handleChange} onKeyDown={handleKeyDown} placeholder="Enter Location" type="text" /> ); }
调整ApiHandler(修正错误)
原代码中console.log(res.data)错误,OpenWeather响应无data属性,同时增加参数编码与错误处理:
export default async function ApiHandler(location) { try { const data = await fetch( `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(location)}&units=imperial&appid=${process.env.API_KEY}` ); if (!data.ok) { throw new Error(`API请求失败,状态码:${data.status}`); } const res = await data.json(); console.log(res); return res; } catch (error) { console.error("Error connecting to api:", error); throw error; } }
关键改进点
- 用
useState确保输入值响应式,避免普通变量的同步问题 - 回车事件直接处理URL更新与API调用,去掉冗余全局变量
useEffect监听URL参数,保证输入框与URL状态同步- 对输入值做
trim()处理,避免无效请求 encodeURIComponent处理位置参数,防止特殊字符导致API请求失败- 修正ApiHandler的响应处理逻辑,正确捕获并抛出错误
内容的提问来源于stack exchange,提问作者Marco Antonio Martinez
相关产品推荐
相关产品推荐

